如何在Chart.js y轴标签旁边添加一个输入框?

问题描述 投票:0回答:1

我使用Chart.js创建了一个实时投票,并希望用户能够通过选择y轴标签左侧的复选框来选择他们的选项。我无法通过Chart.js找出正确的方法

我的第一次尝试是将输入框放在画布之外,但随着它们的增长,它与动态轮询选项的高度不可靠。这是非常糟糕的,因为条形图的高度将根据有多少选项而变化,因此输入框变得一团糟,因为显示的选项是动态的。

这是我最终要在前端实现的图像。如果用户选择该框,则轮询增长以反映用户选择的选择。

enter image description here

下面发表的是我目前陷入困境的代码。我一直坚持试图解决这个问题很长一段时间了。任何帮助将不胜感激!

import { Component, OnInit } from '@angular/core';
// import { Chart } from 'chart.js';
import * as Ably from 'ably';
import * as Chart from 'chart.js';
import { AngularFirestore } from '@angular/fire/firestore';
import { Observable, Subject } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { FirebaseService } from '../services/firebase.service';

@Component({
  selector: 'app-vote-chart',
  templateUrl: './vote-chart.component.html',
  styleUrls: ['./vote-chart.component.scss']
})
export class VoteChartComponent implements OnInit {
  // Attributes
  ably: any
  receiveChanel: any
  chart: any
  users: any
  polls:any = [];
  poll:any;
  votes:any = [];
  labels:any = [];
  poll_type:string = "";

  constructor(db: AngularFirestore, private firebaseService: FirebaseService) {}

  ngOnInit() {
    this.firebaseService.getPoll("key").subscribe(res => {
      const poll_data:any = res.payload.data();
      this.poll = {
        id: res.payload.id,
        helper_text: poll_data.helper_text,
        poll_type: poll_data.poll_type,
        scoring_type: poll_data.scoring_type,
        user: poll_data.user.id,
        choices: []
      };
      this.poll_type = this.poll.poll_type == 2 ? "Pick One" : "Pick Two";
      this.firebaseService.getChoices('key').subscribe(res => {
        res.forEach((choice) => {
          const choice_data:any = choice.payload.doc.data()
          this.poll.choices.push({
            id: choice.payload.doc.id,
            text: choice_data.text,
            votes: choice_data.votes
          });
          this.votes.push(choice_data.votes);
          this.labels.push(choice_data.text);
        });
        console.log("Poll updated!", this.poll);
      });
    });
    this.ably = new Ably.Realtime("key")
    // Attach to channel
    this.receiveChanel = this.ably.channels.get('vote-channel12')
    // Ably subscription
    this.receiveChanel.subscribe("update", (message: any) => {

      var canvas =  <HTMLCanvasElement> document.getElementById("chartjs-2")
      var ctx = canvas.getContext("2d");

      this.chart = new Chart(ctx, {
        type: 'horizontalBar',
        data: {
          labels: this.labels,
          datasets: [{
            label: this.poll_type,
            data: this.votes,
            fill: false,
            backgroundColor: [
              "rgba(255, 99, 132, 0.2)",
              "rgba(255, 159, 64, 0.2)",
              "rgba(255, 205, 86, 0.2)",
              "rgba(75, 192, 192, 0.2)",
              "rgba(54, 162, 235, 0.2)",
              "rgba(153, 102, 255, 0.2)",
              "rgba(201, 203, 207, 0.2)"

            ],
            borderColor: [
              "rgb(255, 99, 132)",
              "rgb(255, 159, 64)",
              "rgb(255, 205, 86)",
              "rgb(75, 192, 192)",
              "rgb(54, 162, 235)",
              "rgb(153, 102, 255)",
              "rgb(201, 203, 207)"
            ],
            borderWidth: 1
          }]
        },
        options: {
          events: ["touchend", "click", "mouseout"],
          onClick: function(e) {
            console.log("clicked!", e);
          },
          tooltips: {
            enabled: true
          },
          title: {
            display: true,
            text: this.poll_type,
            fontSize: 14,
            fontColor: '#666'
          },
          legend: {
            display: false
          },
          maintainAspectRatio: true,
          responsive: true,
          scales: {
            xAxes: [{
              ticks: {
                beginAtZero: true,
                precision: 0
              }
            }]
          }
        }
      })
      console.log("Poll", this.poll);
    });
  }
}

模板代码,内联样式,使其更容易审查

<div id="chart_and_labels_container" style="min-width:620px !important;">
  <div style="float:left; line-height: 175px; display: inline-grid; padding-top: 55px; margin: 0;">
    <input type="checkbox" (click)="vote(1)" style="width:57px; height: 30px;"/>
    <input type="checkbox" (click)="vote(-1)" style="width:57px; margin-top: 10px; height: 30px;"/>
    <input type="checkbox" (click)="vote(-1)" style="width:57px; margin-top: 10px; height: 30px;"/>
  </div>
  <div id="canvasDiv" style="width: 425px; float:left;">
  <canvas id="chartjs-2" ></canvas>
  </div>
</div>

**更新**

如果无法在画布中获取输入框,有人可以提供一些建议,以确保输入框与动态选择可靠地对齐吗?即,当您有5个选项时,2个选项的标签文本大小不同。如何以智能可靠的方式将画布外的输入框排成一行?

javascript angular chart.js
1个回答
1
投票

假设Marc的评论是正确的(这似乎就是这种情况),回答你的“什么是对齐输入的替代解决方案”......

Updated Answer

使用display: flex可以更容易地自动调整复选框之间的大小和/或距离,如果包装画布中条形图的容器的高度始终相同,无论有多少选项。

.chart-container {
  display: flex;
}

.chart {
  height: 200px;
}

.checkbox-container {
  display: flex;
  flex-direction: column;
  height: 128px;    /* height - adjust as needed */
  margin-top: 38px; /* positioning - adjust as needed */
  
  background: white;  /* demo purpose only - hide image checkboxes */
  position: absolute; /* demo purpose only - hide image checkboxes */
  left: 10px;         /* demo purpose only - hide image checkboxes */
  padding: 0 8px;     /* demo purpose only - hide image checkboxes */
  z-index: 1;         /* demo purpose only - hide image checkboxes */
} 

.checkbox {
  cursor: pointer;
  height: 100%;
  width: 100%;
  min-width: 16px; /* minimum checkbox size - adjust as needed */
}
<!-- 3 bars example -->
<div class="chart-container">
  <div class="checkbox-container">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
  </div>
  <img class="chart" src="https://i.stack.imgur.com/440p0.png">
</div>

<!-- 4 bars examples -->
<div class="chart-container">
  <div class="checkbox-container">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
  </div>
  <img class="chart" src="https://i.stack.imgur.com/440p0.png">
</div>

<!-- 5 bars examples -->
<div class="chart-container">
  <div class="checkbox-container">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
  </div>
  <img class="chart" src="https://i.stack.imgur.com/440p0.png">
</div>

Original Answer

你可以...

  • position: relative将画布包装在div容器中
  • 使用position: absolute为您的复选框创建一个容器
  • 将您的复选框添加到position: absolute容器中并使用margin-bottom,以便它们与您的图形完美对齐

如果选项的数量影响图表栏的高度,则必须使CSS值相应地改变...

  • javascript:document.querySelector('.checkbox').style.height = ...
  • 使用Angular [style]指令:[style.height.px]="..."
  • 根据复选框容器中的复选框/兄弟节点的数量使用CSS类:https://stackoverflow.com/a/12198561/5583283

下面是一个使用上面第三个选项的工作示例,CSS类根据checkbox-container中的复选框/兄弟的数量更改值。

整个想法是保持复选框高度等于图表栏高度,复选框之间的距离等于图表栏距离的距离。

请注意,对齐的所有像素值都相对于您的预期和可以播放,但我想它会给你全局。

希望能帮助到你!

.chart-container {
  position: relative;
}

.chart {
  height: 200px;
}

.checkbox-container {
  background: white;  /* for demo purpose - hides image checkboxes */
  padding-right: 8px; /* for demo purpose - hides image checkboxes */
  position: absolute;
  top: 44px;          /* adjust as needed */
  left: 8px;          /* adjust as needed */
} 

.checkbox {
  display: block;
  margin-bottom: 15px; /* adjust as needed */
  height: 28px;        /* desired checkbox height */
  width: 28px;         /* desired checkbox width */
  cursor: pointer;
}

/* 5 checkboxes */
.checkbox:first-child:nth-last-child(5),
.checkbox:first-child:nth-last-child(5) ~ .checkbox {
  margin-bottom: 6px; /* adjust as needed */
  height: 18px;       /* desired checkbox height */
  width: 18px;        /* desired checkbox width */
}
<!-- 3 bars example -->
<div class="chart-container">
  <img class="chart" src="https://i.stack.imgur.com/440p0.png">
  <div class="checkbox-container">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
  </div>
</div>

<!-- 5 bars examples - sorry didn't have an image with 5 bars -->
<div class="chart-container">
  <img class="chart" src="https://i.stack.imgur.com/440p0.png">
  <div class="checkbox-container">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
    <input class="checkbox" type="checkbox">
  </div>
</div>
© www.soinside.com 2019 - 2024. All rights reserved.