I'm trying to use Charts.js on an AWS Lambda function to create a chart image (png).
However, for some reason it plots the axes, but no data.
this is my code:
export const plotData = (values: number[]): Buffer | null => {
const canvas = createCanvas(800, 600);
let ctx: ChartItem = canvas as unknown as ChartItem;
const plugin: Plugin = {
id: "customCanvasBackgroundColor",
beforeDraw: (chart: any, _args: any, options: any) => {
const { ctx: context } = chart;
context.save();
context.globalCompositeOperation = "destination-over";
context.fillStyle = options.color || "#99ffff";
context.fillRect(0, 0, chart.width, chart.height);
context.restore();
},
};
const chart = new Chart(ctx, {
type: "line",
data: {
datasets: [
{
label: "ph",
data: values.map((y) => ({
y,
t: new Date(),
})),
borderWidth: 2,
borderColor: "red",
backgroundColor: "rgb(255, 0, 0, 0.5)",
},
],
},
options: {
responsive: false,
animation: false,
scales: {
y: {
beginAtZero: true,
},
},
plugins: {
legend: {
position: "top",
},
title: {
display: true,
text: "TEstuibg",
},
customCanvasBackgroundColor: {
color: "rgba(255,255,255, 1)",
},
},
},
plugins: [plugin],
});
// chart.draw();
chart.update();
return canvas.toBuffer("image/png");
};
And this is what it is rendering when I call plotData([100, 200, 300, 400, 500, 1600]):
I am already disabling animations and responsiveness. Is there something else I need to do?
I would create the image on the onComplete event, than everything should be visible, atleast this works in browsers.
from the documentation: "...The animation configuration provides callbacks which are useful for synchronizing an external draw to the chart animation....", but works surely for your image creating process. link to documentation
...
options: {
animation:{
duration: 0, // "no" animation
onComplete: () => {
...
// create image
...
}
}
}
...
Ofcourse: in this case your function plotData would have to be async or pass a callback function for when the event onComplete fires.
Animated.View is only scaling to the half of the screen width provided.
Secondly, why do I have to give width in the stylesheet for this to work and what is the particular reason for giving it length 1.
If I increase the width in stylesheet to 2 the Progress Bar covers the entire screen width
import React from 'react'
import { View, Animated, StyleSheet, Dimensions, Easing } from 'react-native'
export default class ProgressBar extends React.Component {
state = {
percent: new Animated.Value(0)
}
componentDidMount() {
this.startAnimation()
}
startAnimation = () => {
this.animation = Animated.timing(this.state.percent, {
toValue: 100,
duration: this.props.timeRemaining*1000,
easing: Easing.linear,
useNativeDriver: true,
})
this.animation.start()
}
render() {
return(
<Animated.View
style={[
styles.bar,
{transform: [{
scaleX: this.state.percent.interpolate({
inputRange: [0, 100],
outputRange: [0, Dimensions.get('window').width]
})
}] }
]}
/>
// <View style={[styles.bar, {width: Dimensions.get('window').width}]}/>
)
}
}
const styles = StyleSheet.create({
bar: {
height: 30,
width: 1,
backgroundColor: 'tomato',
}
})
as I said in Animated View scaling only half of the screen width half of your <Animated.View> is out of your screen and you are not able to see it.
use TranslateX to solve it.
this is working for me:
import * as React from 'react';
import {
Animated,
Dimensions
} from 'react-native';
export default function () {
const timer = new Animated.Value(0);
const { width } = Dimensions.get("window");
const startAnimation = () => {
Animated.timing(timer, {
duration: 2000,
toValue: 1,
useNativeDriver: true,
isInteraction: false,
}).start();
};
React.useEffect(() => {
startAnimation()
}, []);
return (
<Animated.View
style={[
{
width,
height: 5,
backgroundColor: 'blue',
transform: [
{
translateX: timer.interpolate(
{
inputRange: [0, 1],
outputRange: [-width / 2, 0],
}
),
},
{
// you can use scaleX : timer if you don't need interpolation
scaleX: timer.interpolate(
{
inputRange: [0, 1],
outputRange: [0, 1],
}
),
},
],
},
]}
/>
);
}
Link to jsFiddle
google.charts.load('current', {
'packages': ['corechart']
});
//Input data
var data = [
['Data', 'CAT1', 'CAT2', 'CAT3', 'CAT4'],
['Provisions', 5, 0, 0, 0],
];
// Set a callback to run when the Google Visualization API is loaded.
google.charts.setOnLoadCallback(drawChart);
// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.
function drawChart() {
var options = {
colors: ['#00699B', '#087EB4', '#CBE7F7', '8A6996'],
isStacked: true,
chartArea: {
width: '40%'
},
bar: {
groupWidth: "40%"
},
// tooltip: { isHtml: true },
trigger: 'both',
vAxis: {
gridlines: {
color: '#0000006b',
minValue: 0,
baseline: 0
},
format: "$ #,###"
},
};
var dataTable = google.visualization.arrayToDataTable(data);
//Formatters
var intergerFormatter = new google.visualization.NumberFormat({
groupingSymbol: ",",
fractionDigits: 0
});
for (var i = 0; i < data[0].length; i++) {
intergerFormatter.format(dataTable, i);
}
var view = new google.visualization.DataView(dataTable);
var cols = [0];
for (var i = 1; i < data[0].length; i++) {
cols.push({
sourceColumn: i,
type: "number",
label: data[0][i]
});
cols.push({
calc: createTooltip(i),
type: "string",
role: "tooltip",
});
}
view.setColumns(cols);
var chart = new google.visualization.ColumnChart(document.getElementById('provision_chart'));
chart.draw(view, options);
function createTooltip(col) {
return function(dataTable, row) {
var html = dataTable.getColumnLabel(col) + ":" + "\n";
html += "4 " + dataTable.getValue(row, 0) + "\n";
html += "$ " + intergerFormatter.formatValue(dataTable.getValue(row, col)) + " total" + "\n";
return html;
};
}
}
The grid lines on a stacked bar type google charts are not rendering properly.
As per the data, $5 is recorded against Category1, but when it's rendered the bar is slightly over $5.
Can someone suggest a fix?
removing the option --> format: "$ #,###" -- reveals the problem
although the tick mark displays --> $ 5 -- the actual number used is 4.5
see following working snippet...
google.charts.load('current', {
'packages': ['corechart']
});
//Input data
var data = [
['Data', 'CAT1', 'CAT2', 'CAT3', 'CAT4'],
['Provisions', 5, 0, 0, 0],
];
// Set a callback to run when the Google Visualization API is loaded.
google.charts.setOnLoadCallback(drawChart);
// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.
function drawChart() {
var options = {
colors: ['#00699B', '#087EB4', '#CBE7F7', '8A6996'],
isStacked: true,
chartArea: {
width: '40%'
},
bar: {
groupWidth: "40%"
},
// tooltip: { isHtml: true },
trigger: 'both',
vAxis: {
gridlines: {
color: '#0000006b',
minValue: 0,
baseline: 0
},
//format: "$ #,###"
},
};
var dataTable = google.visualization.arrayToDataTable(data);
//Formatters
var intergerFormatter = new google.visualization.NumberFormat({
groupingSymbol: ",",
fractionDigits: 0
});
for (var i = 0; i < data[0].length; i++) {
intergerFormatter.format(dataTable, i);
}
var view = new google.visualization.DataView(dataTable);
var cols = [0];
for (var i = 1; i < data[0].length; i++) {
cols.push({
sourceColumn: i,
type: "number",
label: data[0][i]
});
cols.push({
calc: createTooltip(i),
type: "string",
role: "tooltip",
});
}
view.setColumns(cols);
var chart = new google.visualization.ColumnChart(document.getElementById('provision_chart'));
chart.draw(view, options);
function createTooltip(col) {
return function(dataTable, row) {
var html = dataTable.getColumnLabel(col) + ":" + "\n";
html += "4 " + dataTable.getValue(row, 0) + "\n";
html += "$ " + intergerFormatter.formatValue(dataTable.getValue(row, col)) + " total" + "\n";
return html;
};
}
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="provision_chart" style="width: 500px; height: 500px;"></div>
to correct, you can add a decimal place to the format --> $ #,##0.0
or provide your own vAxis.ticks in an array --> [0, 1, 2, 3, 4, 5, 6]
see following working snippet...
google.charts.load('current', {
'packages': ['corechart']
});
//Input data
var data = [
['Data', 'CAT1', 'CAT2', 'CAT3', 'CAT4'],
['Provisions', 5, 0, 0, 0],
];
// Set a callback to run when the Google Visualization API is loaded.
google.charts.setOnLoadCallback(drawChart);
// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.
function drawChart() {
var options = {
colors: ['#00699B', '#087EB4', '#CBE7F7', '8A6996'],
isStacked: true,
chartArea: {
width: '40%'
},
bar: {
groupWidth: "40%"
},
// tooltip: { isHtml: true },
trigger: 'both',
vAxis: {
gridlines: {
color: '#0000006b',
minValue: 0,
baseline: 0
},
format: "$ #,###",
ticks: [0, 1, 2, 3, 4, 5, 6]
},
};
var dataTable = google.visualization.arrayToDataTable(data);
//Formatters
var intergerFormatter = new google.visualization.NumberFormat({
groupingSymbol: ",",
fractionDigits: 0
});
for (var i = 0; i < data[0].length; i++) {
intergerFormatter.format(dataTable, i);
}
var view = new google.visualization.DataView(dataTable);
var cols = [0];
for (var i = 1; i < data[0].length; i++) {
cols.push({
sourceColumn: i,
type: "number",
label: data[0][i]
});
cols.push({
calc: createTooltip(i),
type: "string",
role: "tooltip",
});
}
view.setColumns(cols);
var chart = new google.visualization.ColumnChart(document.getElementById('provision_chart'));
chart.draw(view, options);
function createTooltip(col) {
return function(dataTable, row) {
var html = dataTable.getColumnLabel(col) + ":" + "\n";
html += "4 " + dataTable.getValue(row, 0) + "\n";
html += "$ " + intergerFormatter.formatValue(dataTable.getValue(row, col)) + " total" + "\n";
return html;
};
}
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="provision_chart" style="width: 500px; height: 500px;"></div>
the getColumnRange(colIndex) method can assist in building the ticks dynamically
the method returns an object {} with properties for min and max for the column index provided
see following working snippet for an example...
google.charts.load('current', {
'packages': ['corechart']
});
//Input data
var data = [
['Data', 'CAT1', 'CAT2', 'CAT3', 'CAT4'],
['Provisions', 5, 0, 0, 0],
];
// Set a callback to run when the Google Visualization API is loaded.
google.charts.setOnLoadCallback(drawChart);
// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.
function drawChart() {
var options = {
colors: ['#00699B', '#087EB4', '#CBE7F7', '8A6996'],
isStacked: true,
chartArea: {
width: '40%'
},
bar: {
groupWidth: "40%"
},
// tooltip: { isHtml: true },
trigger: 'both',
vAxis: {
gridlines: {
color: '#0000006b',
minValue: 0,
baseline: 0
},
format: "$ #,###"
},
};
var dataTable = google.visualization.arrayToDataTable(data);
//Formatters
var intergerFormatter = new google.visualization.NumberFormat({
groupingSymbol: ",",
fractionDigits: 0
});
for (var i = 0; i < data[0].length; i++) {
intergerFormatter.format(dataTable, i);
}
var view = new google.visualization.DataView(dataTable);
var cols = [0];
var ticksY = [];
var maxY = null;
var minY = null;
for (var i = 1; i < view.getNumberOfColumns(); i++) {
var range = view.getColumnRange(i);
if (maxY === null) {
maxY = Math.ceil(range.max);
} else {
maxY = Math.max(maxY, Math.ceil(range.max));
}
if (minY === null) {
minY = Math.floor(range.min);
} else {
minY = Math.min(minY, Math.floor(range.min));
}
}
for (var i = minY; i <= maxY + 1; i++) {
ticksY.push(i);
}
options.vAxis.ticks = ticksY;
for (var i = 1; i < data[0].length; i++) {
cols.push({
sourceColumn: i,
type: "number",
label: data[0][i]
});
cols.push({
calc: createTooltip(i),
type: "string",
role: "tooltip",
});
}
view.setColumns(cols);
var chart = new google.visualization.ColumnChart(document.getElementById('provision_chart'));
chart.draw(view, options);
function createTooltip(col) {
return function(dataTable, row) {
var html = dataTable.getColumnLabel(col) + ":" + "\n";
html += "4 " + dataTable.getValue(row, 0) + "\n";
html += "$ " + intergerFormatter.formatValue(dataTable.getValue(row, col)) + " total" + "\n";
return html;
};
}
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="provision_chart" style="width: 500px; height: 500px;"></div>
I would like to use jquery ui slider with a knockout observable.
All the examples I find uses knockout 2x and fail when I try with knockout 3.4
Does anyone have an example I could use?
Here is the code I found for knockout 2.x
<div data-bind="foreach: display_timers_for_this_queue().timers">
Timer: <input data-bind="value: timer, valueUpdate: 'afterkeydown'" />
<div style="margin: 2px; height: 30px;" data-bind="slider: timer, sliderOptions: {min: 0, max: 100, range: 'min', step: 1}"></div>
</div>
ko.bindingHandlers.slider = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().sliderOptions || {};
$(element).slider(options);
ko.utils.registerEventHandler(element, "slidechange", function (event, ui) {
var observable = valueAccessor();
observable(ui.value);
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).slider("destroy");
});
ko.utils.registerEventHandler(element, "slide", function (event, ui) {
var observable = valueAccessor();
observable(ui.value);
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (isNaN(value)) value = 0;
$(element).slider("value", value);
}
};
versions used
jquery-ui-1.12.0
knockout-3.4.0
This is using knockout 3.3.0
<div data-bind="slider: MyObservable,
sliderOptions: {min: 0, max: 100, range: 'min', step: 1,
start: function(){//custom logic},
stop: function(event, ui){//custom logic}}"></div>
ko.bindingHandlers.slider = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().sliderOptions || {};
$(element).slider(options);
ko.utils.registerEventHandler(element, "slidechange", function (event, ui) {
var observable = valueAccessor();
observable(ui.value);
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).slider("destroy");
});
ko.utils.registerEventHandler(element, "slide", function (event, ui) {
var observable = valueAccessor();
observable(ui.value);
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (isNaN(value)) value = 0;
//custom logic
$(element).slider("value", value);
}
};
I'm trying to set min max attributes for inputSurface of type range, but its not working. The generated input element doesn't have min/max attributes.
My code:
var moveDown = this.options.flyerSheetHeight + this.options.flyerItemHeight + 2;
var downModifier = new StateModifier({
transform: Transform.translate(0, moveDown, 0)
});
var flyerItemGenSurface = new InputSurface({
size: [this.options.flyerSheetWidth, 5],
type: 'range',
min: 1,
max: 10,
properties: {
zIndex: 1,
}
});
this.mainNode.add(downModifier).add(flyerItemGenSurface);
try this!
var moveDown = this.options.flyerSheetHeight + this.options.flyerItemHeight + 2;
var downModifier = new StateModifier({
transform: Transform.translate(0, moveDown, 0)
});
var flyerItemGenSurface = new InputSurface({
size: [this.options.flyerSheetWidth, 5],
type: 'range',
properties: {
zIndex: 1,
},
attributes: {
min: 1,
max: 10
}
});
this.mainNode.add(downModifier).add(flyerItemGenSurface);
edit: woops