Not working dynamically rendering my chart on vue-chartjs - javascript

I was define my chart as below (MainChart.vue).
import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
// const brandPrimary = '#20a8d8'
export default {
extends: Line,
mixins: [reactiveProp],
props: ['options', 'chartData', 'height'],
mounted () {
this.renderChart(this.chartData, this.options)
var elements = 1
}
}
I tested this code and confirmed that it worked well.
<line-chart :chartData="myChartData"></line-chart>
but, I tried rendering chart dynamically, it is not working.
import lineChart from './MainChart';
// ...
let chartClass = Vue.extend(lineChart)
let chartInstance = new chartClass({
propsData: {
chartData: myChartData
}
})
chartInstance.$mount()
console.log(chartInstance.$el)
console.log(chartInstance.$el.querySelector("canvas").toDataURL('image/png'))
console.log(chartInstance.$refs.canvas)
console.log(chartInstance.$refs.canvas.toDataURL('image/png'))
Console messages:
I checked from the console and found that nothing was drawn in the canvas area.
How can I do render my chart dynamically?
Similar questions:
Is it possible to print a chart with vue-chartjs?

To get full image data, you have to wait until the chart is finished. Using 'Promise' is helpful.
async function addChart(d, i, w, h) {
var canvas = document.createElement("canvas")
canvas.width = 765
canvas.height = 382
//canvas.style.width = "765px"
//canvas.style.height = "382px"
//canvas.style.display = "none"
canvas.id = "dynamicChart"
document.body.appendChild(canvas)
var ctx = document.getElementById("dynamicChart").getContext('2d');
var draw = () => new Promise((resolve, reject) => {
new Chart(ctx, {
type: 'bar',
data: d,
options: {
responsive: false
}
})
setTimeout(() => resolve(), 100)
})
await draw()
let imageData = document.getElementById("dynamicChart").toDataURL("image/png")
console.log(imageData)
addImage(imageData, i, w, h)
document.body.removeChild(canvas)
}
// ...
await addChart(myChartData, 0, 400, 300)
If you want draw multiple chart for in the loop, try this:
let chartFunctions = []
myList.forEach((item) => {
chartFunctions.push(async function() {
await addChart(myChartData, 3, 160, 80)
})
}
for(let k in chartFunctions) {
await chartFunctions[k]()
}
Console messages:

Related

How to put variables in Vue

i'm learning vue and i have small problem.
I've created code, which receive some informations from webserwer (via socket) and this code works fine.
But i would like to do very simple thing - display info as variable in HTML and i have problem with it.
My code is:
export default {
components: {
BCard,
BButton,
},
data() {
return {
connection: null,
json: {
cmd: 'start',
width: 1024,
height: 800,
url: 'https://www.google.com',
token: '',
service_id: 1,
thumbnail_width: 100,
thumbnail_height: 100,
},
}
},
created() {
console.log('Starting Connection to WebSocket')
this.connection = new WebSocket('ws://127.0.0.1:8080/')
// this.connection = new WebSocket('ws://echo.websocket.org')
this.connection.onopen = function (event) {
console.log(event)
console.log('Success')
}
this.connection.onmessage = webSocketOnMSG
},
methods: {
sendMessage(message) {
console.log(this.connection)
console.log(message)
console.log(JSON.stringify(message))
const dat = this.connection.send(JSON.stringify(message))
console.log('TT', dat)
},
drawItem() {
const img = document.createElement('img')
const canvas = document.getElementById('canvasId')
img.src = 'http://image...'
img.onload = function (a) {
const h = a.target.height
const w = a.target.width
const c = canvas.getContext('2d')
canvas.width = w
canvas.height = h
c.drawImage(img, 0, 0)
document.getElementById('draw-image-test').appendChild(canvas)
}
},
webSocketOnMSG(msg) {
console.log(msg)
},
},
}
and i would like to add code like this:
data: {
xposition: 'xpos',
yposition: 'ypos'
}
but when i'm adding it to created earlier data() i have error, so this doesn't work:
data() {
xposition: 'xpos',
yposition: 'ypos',
return {...}
}
where should i add code to replace variables {{xposition}} and {{yposition}} in HMTL?
You must put your new variables inside your returned object in the data function, alongside your 'json' variable. You need to declare them first as empty values, and then add the proper values in your API call callback
data() {
return {
xposition: '',
yposition: '',
...
}
}
webSocketOnMSG(msg) {
// this will change your component's xposition property
this.xposition = msg.propertyYouWantToAccess
},

Adding a constructor to an injected Project Class

I'm trying to add a constructor, which I call ctor, to Project, so I can add to it some properties when it's instantiated.
Here's the (absolutely ghastly) hack I'm using on Item to make this tick:
const canvas = document.querySelector('canvas')
paper.setup(canvas)
paper.Item.inject({
ctor: function(args) {
console.log('Item constructed!')
}
})
// All Items are supposed to call `_initialize` so we "hook" there.
const Item_initialize = paper.Item.prototype._initialize
const Item_ctor = paper.Item.prototype.ctor
paper.Item.prototype._initialize = function(...args) {
const initializer = Item_initialize.apply(this, args)
if (Item_ctor) Item_ctor.apply(this)
return initializer
}
const path = new paper.Path.Line({
strokeColor: 'black',
strokeWidth: 5,
from: 100,
to: 50
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-core.js"></script>
<canvas></canvas>
Unfortunately the above trick doesn't work on Project:
const canvas = document.querySelector('canvas')
paper.setup(canvas)
paper.Project.inject({
ctor: function(args) {
// Not called
console.log('Project constructed!')
}
})
const Project_initialize = paper.Project.prototype._initialize
const Project_ctor = paper.Project.prototype.ctor
paper.Project.prototype._initialize = function(...args) {
const initializer = Project_initialize.apply(this, args)
if (Project_ctor) Project_ctor.apply(this)
return initializer
}
const project = new paper.Project(canvas)
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-core.js"></script>
<canvas></canvas>
What am I missing here?

VTK.js VR not display/showing an hand controller in Room

I try to use VTK VR, but When I test it, I cant see controller in room, I can get action from controller like press or somthing, but without its seen for user in VR mode, how I can fix it?
Online Demo From Here.
import 'vtk.js/Sources/favicon';
import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor';
import vtkCalculator from 'vtk.js/Sources/Filters/General/Calculator';
import vtkConeSource from 'vtk.js/Sources/Filters/Sources/ConeSource';
import vtkFullScreenRenderWindow from 'vtk.js/Sources/Rendering/Misc/FullScreenRenderWindow';
import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper';
import { AttributeTypes } from 'vtk.js/Sources/Common/DataModel/DataSetAttributes/Constants';
import { FieldDataTypes } from 'vtk.js/Sources/Common/DataModel/DataSet/Constants';
import controlPanel from './controller.html';
// ----------------------------------------------------------------------------
// Standard rendering code setup
// ----------------------------------------------------------------------------
const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({
background: [0, 0, 0],
});
const renderer = fullScreenRenderer.getRenderer();
const renderWindow = fullScreenRenderer.getRenderWindow();
// ----------------------------------------------------------------------------
// Example code
// ----------------------------------------------------------------------------
// create a filter on the fly, sort of cool, this is a random scalars
// filter we create inline, for a simple cone you would not need
// this
// ----------------------------------------------------------------------------
const coneSource = vtkConeSource.newInstance({ height: 100.0, radius: 50.0 });
// const coneSource = vtkConeSource.newInstance({ height: 1.0, radius: 0.5 });
const filter = vtkCalculator.newInstance();
filter.setInputConnection(coneSource.getOutputPort());
// filter.setFormulaSimple(FieldDataTypes.CELL, [], 'random', () => Math.random());
filter.setFormula({
getArrays: (inputDataSets) => ({
input: [],
output: [
{
location: FieldDataTypes.CELL,
name: 'Random',
dataType: 'Float32Array',
attribute: AttributeTypes.SCALARS,
},
],
}),
evaluate: (arraysIn, arraysOut) => {
const [scalars] = arraysOut.map((d) => d.getData());
for (let i = 0; i < scalars.length; i++) {
scalars[i] = Math.random();
}
},
});
const mapper = vtkMapper.newInstance();
mapper.setInputConnection(filter.getOutputPort());
const actor = vtkActor.newInstance();
actor.setMapper(mapper);
actor.setPosition(20.0, 0.0, 0.0);
renderer.addActor(actor);
renderer.resetCamera();
renderWindow.render();
// -----------------------------------------------------------
// UI control handling
// -----------------------------------------------------------
fullScreenRenderer.addController(controlPanel);
const representationSelector = document.querySelector('.representations');
const resolutionChange = document.querySelector('.resolution');
const vrbutton = document.querySelector('.vrbutton');
representationSelector.addEventListener('change', (e) => {
const newRepValue = Number(e.target.value);
actor.getProperty().setRepresentation(newRepValue);
renderWindow.render();
});
resolutionChange.addEventListener('input', (e) => {
const resolution = Number(e.target.value);
coneSource.setResolution(resolution);
renderWindow.render();
});
vrbutton.addEventListener('click', (e) => {
if (vrbutton.textContent === 'Send To VR') {
fullScreenRenderer.getOpenGLRenderWindow().startVR();
vrbutton.textContent = 'Return From VR';
} else {
fullScreenRenderer.getOpenGLRenderWindow().stopVR();
vrbutton.textContent = 'Send To VR';
}
});
// -----------------------------------------------------------
// Make some variables global so that you can inspect and
// modify objects in your browser's developer console:
// -----------------------------------------------------------
global.source = coneSource;
global.mapper = mapper;
global.actor = actor;
global.renderer = renderer;
global.renderWindow = renderWindow;
Note: Tested on Latest Version of Chrome & Firefox (64 bit). Controller Type is HTC Vive.
Update 1:
We found this option not development, so that I share updated link now Github Link

Variable is pushed, but it doesn't exist later

I have the following code:
const scenarioList = []
const randomScenario = () => {
return scenarioList[Math.floor(Math.random() * scenarioList.length--)]
}
class Scenario{
setBG(){
//screen.bg = this.bg
//screen.redraw()
}
write(text, buttons, callback){
//$('#gametext > span').html(`<span>${text}</span>`)
//input.setText(buttons)
//input.bindAll(callback)
}
constructor(imgsrc, text, actions, callback){
let img = new Image()
img.src = imgsrc
this.bg = img
this.text = text
this.actions = actions
this.callback = callback
scenarioList.push(this)
console.log(scenarioList)
}
}
I init the class the following (and this is in the global scope)
new Scenario('./bg/1.png', 'You look around and see a huge mountain, what do you do?',[
'Climb It!!',
'Walk around',
'Other Direction',
'Rest',
], [
() => {
alert('a')
},
() => {
alert('a')
},
() => {
alert('a')
},
() => {
alert('a')
},
])
And verify with console.log(scenarioList)
[Scenario]
So its appended, but when I later try to do a console.log() on the same variable it is the following:
[]
Code that causes it:
const startGame = () => {
alert('were here') // this executes at the correct time, but later then variable init.
let scn = randomScenario()
console.log(scenarioList)
scn.write()
scn.setBG()
}
I am not seeing why this would happen, anyone can give me a push in the right direction?
I've found the solution, this code actually removed the element from the array:
const randomScenario = () => {
return scenarioList[Math.floor(Math.random() * scenarioList.length--)]
}
instead I did this:
return scenarioList[Math.floor(Math.random() * scenarioList.length -1)]

How do I selecting a date range (like onClick but drag/select)

I'd like to rewrite vizwit using Chart.js, and I'm having a hard time figuring out how to get the date/time chart interaction to work. If you try selecting a date range on this demo, you'll see that it filters the other charts. How do I get Chart.js to let me select a range like that on its time scale chart? It seems like by default it only lets me click on a specific date point.
Thanks for your time.
Building on #jordanwillis's and your answers, you can easily achieve anything you want, by placing another canvas on top on your chart.
Just add pointer-events:none to it's style to make sure it doesn't intefere with the chart's events.
No need to use the annotations plugin.
For example (in this example canvas is the original chart canvas and overlay is your new canvas placed on top):
var options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
borderWidth: 1
},
{
label: '# of Points',
data: [7, 11, 5, 8, 3, 7],
borderWidth: 1
}
]
},
options: {
scales: {
yAxes: [{
ticks: {
reverse: false
}
}]
}
}
}
var canvas = document.getElementById('chartJSContainer');
var ctx = canvas.getContext('2d');
var chart = new Chart(ctx, options);
var overlay = document.getElementById('overlay');
var startIndex = 0;
overlay.width = canvas.width;
overlay.height = canvas.height;
var selectionContext = overlay.getContext('2d');
var selectionRect = {
w: 0,
startX: 0,
startY: 0
};
var drag = false;
canvas.addEventListener('pointerdown', evt => {
const points = chart.getElementsAtEventForMode(evt, 'index', {
intersect: false
});
startIndex = points[0]._index;
const rect = canvas.getBoundingClientRect();
selectionRect.startX = evt.clientX - rect.left;
selectionRect.startY = chart.chartArea.top;
drag = true;
// save points[0]._index for filtering
});
canvas.addEventListener('pointermove', evt => {
const rect = canvas.getBoundingClientRect();
if (drag) {
const rect = canvas.getBoundingClientRect();
selectionRect.w = (evt.clientX - rect.left) - selectionRect.startX;
selectionContext.globalAlpha = 0.5;
selectionContext.clearRect(0, 0, canvas.width, canvas.height);
selectionContext.fillRect(selectionRect.startX,
selectionRect.startY,
selectionRect.w,
chart.chartArea.bottom - chart.chartArea.top);
} else {
selectionContext.clearRect(0, 0, canvas.width, canvas.height);
var x = evt.clientX - rect.left;
if (x > chart.chartArea.left) {
selectionContext.fillRect(x,
chart.chartArea.top,
1,
chart.chartArea.bottom - chart.chartArea.top);
}
}
});
canvas.addEventListener('pointerup', evt => {
const points = chart.getElementsAtEventForMode(evt, 'index', {
intersect: false
});
drag = false;
console.log('implement filter between ' + options.data.labels[startIndex] + ' and ' + options.data.labels[points[0]._index]);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.js"></script>
<body>
<canvas id="overlay" width="600" height="400" style="position:absolute;pointer-events:none;"></canvas>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
</body>
Notice we're basing our events and coordinates on the original canvas, but we draw on the overlay. This way we don't mess the chart's functionality.
For all of you interested in Jony Adamits solution, I created a ChartJs plugin based on his implementation. Additionaly I fixed some minor issues in regard to resizing the chart and detection of the selected data points.
Feel free to use it or to create a plugin github repo for it.
Installation
import "chart.js";
import {Chart} from 'chart.js';
import {ChartJsPluginRangeSelect} from "./chartjs-plugin-range-select";
Chart.pluginService.register(new ChartJsPluginRangeSelect());
Configuration
let chartOptions = rangeSelect: {
onSelectionChanged: (result: Array<Array<any>>) => {
console.log(result);
}
}
Plugin Code
import {Chart, ChartSize, PluginServiceGlobalRegistration, PluginServiceRegistrationOptions} from "chart.js";
interface ChartJsPluginRangeSelectExtendedOptions {
rangeSelect?: RangeSelectOptions;
}
interface RangeSelectOptions {
onSelectionChanged?: (filteredDataSets: Array<Array<any>>) => void;
fillColor?: string | CanvasGradient | CanvasPattern;
cursorColor?: string | CanvasGradient | CanvasPattern;
cursorWidth?: number;
state?: RangeSelectState;
}
interface RangeSelectState {
canvas: HTMLCanvasElement;
}
interface ActiveSelection {
x: number;
w: number;
}
export class ChartJsPluginRangeSelect implements PluginServiceRegistrationOptions, PluginServiceGlobalRegistration {
public id = 'rangeSelect';
beforeInit(chartInstance: Chart, options?: any) {
const opts = (chartInstance.config.options as ChartJsPluginRangeSelectExtendedOptions);
if (opts.rangeSelect) {
const canvas = this.createOverlayCanvas(chartInstance);
opts.rangeSelect = Object.assign({}, opts.rangeSelect, {state: {canvas: canvas}});
chartInstance.canvas.parentElement.prepend(canvas);
}
}
resize(chartInstance: Chart, newChartSize: ChartSize, options?: any) {
const rangeSelectOptions = (chartInstance.config.options as ChartJsPluginRangeSelectExtendedOptions).rangeSelect;
if (rangeSelectOptions) {
rangeSelectOptions.state.canvas.width = newChartSize.width;
rangeSelectOptions.state.canvas.height = newChartSize.height;
}
}
destroy(chartInstance: Chart) {
const rangeSelectOptions = (chartInstance.config.options as ChartJsPluginRangeSelectExtendedOptions).rangeSelect;
if (rangeSelectOptions) {
rangeSelectOptions.state.canvas.remove();
delete rangeSelectOptions.state;
}
}
private createOverlayCanvas(chart: Chart): HTMLCanvasElement {
const rangeSelectOptions = (chart.config.options as ChartJsPluginRangeSelectExtendedOptions).rangeSelect;
const overlay = this.createOverlayHtmlCanvasElement(chart);
const ctx = overlay.getContext('2d');
let selection: ActiveSelection = {x: 0, w: 0};
let isDragging = false;
chart.canvas.addEventListener('pointerdown', evt => {
const rect = chart.canvas.getBoundingClientRect();
selection.x = this.getXInChartArea(evt.clientX - rect.left, chart);
isDragging = true;
});
chart.canvas.addEventListener('pointerleave', evt => {
if (!isDragging) {
ctx.clearRect(0, 0, overlay.width, overlay.height);
}
});
chart.canvas.addEventListener('pointermove', evt => {
ctx.clearRect(0, 0, chart.canvas.width, chart.canvas.height);
const chartContentRect = chart.canvas.getBoundingClientRect();
const currentX = this.getXInChartArea(evt.clientX - chartContentRect.left, chart);
if (isDragging) {
selection.w = currentX - selection.x;
ctx.fillStyle = rangeSelectOptions.fillColor || '#00000044';
ctx.fillRect(selection.x, chart.chartArea.top, selection.w, chart.chartArea.bottom - chart.chartArea.top);
} else {
const cursorWidth = rangeSelectOptions.cursorWidth || 1;
ctx.fillStyle = rangeSelectOptions.cursorColor || '#00000088';
ctx.fillRect(currentX, chart.chartArea.top, cursorWidth, chart.chartArea.bottom - chart.chartArea.top);
}
});
chart.canvas.addEventListener('pointerup', evt => {
const onSelectionChanged = rangeSelectOptions.onSelectionChanged;
if (onSelectionChanged) {
onSelectionChanged(this.getDataSetDataInSelection(selection, chart));
}
selection = {w: 0, x: 0};
isDragging = false;
ctx.clearRect(0, 0, overlay.width, overlay.height);
});
return overlay;
}
private createOverlayHtmlCanvasElement(chartInstance: Chart): HTMLCanvasElement {
const overlay = document.createElement('canvas');
overlay.style.position = 'absolute';
overlay.style.pointerEvents = 'none';
overlay.width = chartInstance.canvas.width;
overlay.height = chartInstance.canvas.height;
return overlay;
}
private getXInChartArea(val: number, chartInstance: Chart) {
return Math.min(Math.max(val, chartInstance.chartArea.left), chartInstance.chartArea.right);
}
private getDataSetDataInSelection(selection: ActiveSelection, chartInstance: Chart): Array<any> {
const result = [];
const xMin = Math.min(selection.x, selection.x + selection.w);
const xMax = Math.max(selection.x, selection.x + selection.w);
for (let i = 0; i < chartInstance.data.datasets.length; i++) {
result[i] = chartInstance.getDatasetMeta(i)
.data
.filter(data => xMin <= data._model.x && xMax >= data._model.x)
.map(data => chartInstance.data.datasets[i].data[data._index]);
}
return result;
}
}
Unfortunately, nothing like this is built into chart.js. You would have to implement your own event hooks and handlers that would render a highlighted section on a chart and then use the .getElementsAtEvent(e) prototype method to figure out what data has been highlighted. Even these hooks that are built in may not be enough to implement what you are wanting.
Event hook options are:
Add event handlers on the canvas element itself (see example below)
canvas.onclick = function(evt){
var activePoints = myLineChart.getElementsAtEvent(evt);
// => activePoints is an array of points on the canvas that are at the same position as the click event.
};
Add event handler on the chart.js chart object using the onClick config option (explained here).
Extend some of the core charts event hooks and add your own. (see here for some guidance).
Assuming this approach works, then you could then filter your original chart data array accordingly (in the underlying chart.js object) and call the .update() prototype method to paint a new chart.
Update a few months later based on #jordanwillis' answer: I've got the beginnings of range selection.
canvas.onpointerdown = function (evt) {
clearAnnotations()
const points = chart.getElementsAtEventForMode(evt, 'index', { intersect: false })
const label = chart.data.labels[points[0]._index]
addAnnotation(label)
}
canvas.onpointerup = function (evt) {
const points = chart.getElementsAtEventForMode(evt, 'index', { intersect: false })
const label = chart.data.labels[points[0]._index]
addAnnotation(label)
}
function clearAnnotations () {
if (chart.options.annotation) {
chart.options.annotation.annotations = []
}
}
function addAnnotation (label) {
const annotation = {
scaleID: 'x-axis-0',
type: 'line',
mode: 'vertical',
value: label,
borderColor: 'red'
}
chart.options.annotation = chart.options.annotation || {}
chart.options.annotation.annotations = chart.options.annotation.annotations || []
chart.options.annotation.annotations.push(annotation)
chart.update()
}
Still need to figure out how to show a visual hover indicator as in the demo linked in the question, but it's a start.
The people who made ChartJS also made a plugin called chartjs-plugin-zoom. To install the plugin type:
npm install chartjs-plugin-zoom.
Implement:
import { Chart } from 'chart.js';
import zoomPlugin from 'chartjs-plugin-zoom';
Chart.register(zoomPlugin);
To add zooming functionality by dragging, add this to the chart configuration:
options: {
plugins: {
zoom: {
pan: {
enabled: true,
mode: 'x',
modifierKey: 'ctrl',
},
zoom: {
drag: {
enabled: true
},
mode: 'x'
}
}
}
}
A more thorough installation and use tutorial can be found here.
Instructions on how to implement zooming functionality can be found here.

Categories