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

时间:2019-03-21 22:30:06

标签: javascript angular chart.js

我已经使用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>

**更新**

如果无法在画布上获得输入框,那么有人可以提供一些最佳建议以确保输入框与动态选择可靠地对齐吗?即2个选项的标签文本大小与5个选项的标签文本大小不同。如何以一种智能,可靠的方式在画布外部排列输入框?

1 个答案:

答案 0 :(得分:1)

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

更新的答案

如果在画布中包裹条形的容器的高度始终相同,则使用display: flex使得更容易自动调整复选框之间的大小和/或距离,无论有多少选择。 / p>

.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>

原始答案

你可以...

  • 使用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

下面是一个使用上面第3个选项的工作示例,这些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>