Konva.js - draw on stage in time interval loop - konvajs

I am trying to draw multiple rectangles in time loop every 250ms using Konva.js, but the canvas updates only after loop is finish. What am I doing wrong?
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
stage.add(layer);
for (let i=0; i < 10; i++){
var rect1 = new Konva.Rect({
x: i*25,
y: 20,
width: 20,
height: 20,
fill: 'red'
});
layer.add(rect1);
layer.draw();
sleep(250);
}

Your sleep method blocks main JS thread. You need to use async methods to wait. There are many ways to do that. With modern js you can do this:
// noprotect
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const delay = (time) => new Promise(resolve => setTimeout(resolve, time));
async function startAddingShapes() {
for (let i=0; i < 10; i++){
const rect = new Konva.Rect({
x: i*25,
y: 20,
width: 20,
height: 20,
fill: 'red'
});
layer.add(rect);
await delay(250);
}
}
startAddingShapes();
Demo: https://jsbin.com/sahamulako/1/edit?html,js,output

Related

Highcharts export server layout algorithm error

I am using the highcharts export server node module to export treemap charts. I'm trying to create a custom layoutAlgorithm. I already have this working on the client side, but when I move the code to the server I get errors. Here's some example code:
import * as exporter from 'highcharts-export-server';
import Highcharts from 'highcharts';
function quadrants(parent, children) {
let width = parent.width / 2;
let height = parent.height / 2;
let areas = [
{
x: parent.x + width,
y: parent.y,
width,
height,
},
{
x: parent.x,
y: parent.y,
width,
height,
},
{
x: parent.x,
y: parent.y + height,
width,
height,
},
{
x: parent.x + width,
y: parent.y + height,
width,
height,
},
];
for (let i = 0; i < children.length; i++) {
areas.push();
}
return areas;
}
// This is the problem line
Highcharts.seriesTypes.treemap.prototype.quadrants = quadrants;
const exportSettings = {
// ...
series: [
type: 'treemap',
levels: [{
level: 1,
layoutAlgorithm: 'quadrants',
}],
],
// ...
}
return new Promise((resolve, reject) => {
exporter.export(exportSettings, (err, res) => {
if (err) reject(err);
resolve(res);
});
});
When I run this code I get the following error
TypeError: Cannot read property 'treemap' of undefined
Is this the incorrect way to set the layout algorithm or is there another way to set a layout algorithm with the highcharts export server?
Thanks

Html2Canvas doesn't works... the pdf shows empty

When I run the app and I click on the button, the PDF looks empty.
I was looking for by console.log() and the canvas doesn't show anything.
import { Component, OnInit } from '#angular/core';
import * as jspdf from 'jspdf';
import html2canvas from 'html2canvas';
generatePDF(){
html2canvas(document.getElementById('albaran')).then(canvas => {
// Few necessary setting options
var imgWidth = 208;
var pageHeight = 295;
var imgHeight = canvas.height * imgWidth / canvas.width;
var heightLeft = imgHeight;
const contentDataURL = canvas.toDataURL('image/png')
let pdf = new jspdf('p', 'mm', 'a4'); // A4 size page of PDF
var position = 0;
pdf.addImage(contentDataURL, 'PNG', 0, position, imgWidth, imgHeight)
pdf.save('MYPdf.pdf'); // Generated PDF
});
}
}
Finally, I have found a solution. I use jsPDF and dom-to-image libraries.
https://www.npmjs.com/package/jspdf
https://www.npmjs.com/package/dom-to-image
import * as jsPDF from 'jspdf';
import domtoimage from 'dom-to-image';
exportPdf(){
const div = document.getElementById('pdf');
const options = { background: 'white', height: 845, width: 595 };
domtoimage.toPng(div, options).then((dataUrl) => {
//Initialize JSPDF
const doc = new jsPDF('p', 'mm', 'a4');
//Add image Url to PDF
doc.addImage(dataUrl, 'PNG', 0, 0, 210, 340);
doc.save('pdfDocument.pdf');
}
}
Once you click on the button it will take time while loading an element from DOM so using setTimeout it will work.
import * as html2canvas from 'html2canvas';
import * as jspdf from 'jspdf';
generatePDF() {
setTimeout(() => {
const data = document.getElementById('printdiv');
html2canvas(data).then(canvas => {
// Few necessary setting options
const imgWidth = 208;
const pageHeight = 295;
const imgHeight = canvas.height * imgWidth / canvas.width;
let heightLeft = imgHeight;
const contentDataURL = canvas.toDataURL('image/png');
const pdf = new jspdf('p', 'mm', 'a4'); // A4 size page of PDF
let position = 0;
pdf.addImage(contentDataURL, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
// pdf.text(190, 294, '1');
let count = 1;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(contentDataURL, 'PNG', 0, position, imgWidth, imgHeight);
// pdf.text(150, 10, 'this test meaasage');
count++;
// pdf.text(190, 294, count.toString());
heightLeft -= pageHeight;
}
const date = this.datePipe.transform(new Date(), 'dd/MM/yy');
const text = 'Created At :' + date;
pdf.setTextColor(163, 163, 163);
pdf.text(10, 290, text);
// pdf.text(190, 294, count.toString());
const currentuser = this.localstorgeservice.getCurrentUser();
const url = 'URL:' + this.document.location.href;
pdf.text(10, 280, url.toString());
pdf.text(150, 290, currentuser.userid);
pdf.save(this.bankingchannelname + '.pdf'); // Generated PDF
});
}, 700);
}
Here it is,
$(document).click(function () {
domtoimage.toPng(document.body)
.then(function (blob) {
var pdf = new jsPDF('p', 'mm', 'a4');
pdf.addImage(blob,'PNG', 0, 0, 210, 225);
pdf.save("test.pdf");
that.options.api.optionsChanged();
});
});

Highcharts: How we can sort series with animation?

I am using horizontal bar chart with continuous update of series data. This is achieved successfully, but now i want these series to be sorted on data (continuously with every update of series) in desc order with animation. I mean when a bar get max value then move it to the top with animation.
how can i achieve this?
That type of functionality is not supported by default in Highcharts. Below you can find an example that shows how you can achieve the wanted result by custom code:
var options = {
chart: {
type: 'bar'
},
xAxis: {
categories: ['Cat1', 'Cat2', 'Cat3'],
},
series: [{
data: [1000, 900, 800]
}]
};
var chart = Highcharts.chart('container', options);
// Add custom data labels
chart.series[0].points.forEach(function(point, i) {
var x = chart.plotWidth - point.plotY + chart.plotLeft,
y = chart.xAxis[0].ticks[i].label.xy.y;
point.customDataLabel = chart.renderer.text(
point.y,
x,
y
)
.css({
color: '#000000',
fontSize: '14px'
})
.attr({
zIndex: 3
})
.add();
setAlign(point.customDataLabel);
});
function setAlign(label, xPos) {
var align = 'left',
bbox = label.getBBox();
if (chart.chartWidth < (xPos ? xPos : bbox.x + bbox.width) + 50) {
align = 'right';
}
label.attr({
align: align
})
}
var update = function() {
var points = chart.series[0].points;
chart.series[0].setData([Math.round(Math.random() * 1000), Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)]);
};
var rotate = function() {
var points = chart.series[0].points,
labelX,
ticks = chart.xAxis[0].ticks;
var sortedPoints = points.slice();
sortedPoints.sort(function(a, b) {
return b.y - a.y;
});
points.forEach(function(point, i) {
sortedPoints.forEach(function(sPoint, j) {
if (point === sPoint) {
labelX = chart.plotWidth - points[i].plotY + chart.plotLeft;
// Animate the column
points[i].graphic.animate({
x: points[j].shapeArgs.x
});
// Animate the label
points[i].customDataLabel.attr({
text: points[i].y
}).animate({
y: ticks[j].label.xy.y,
x: labelX
});
setAlign(points[i].customDataLabel, labelX);
// Animate the axis label
ticks[i].label.animate({
y: ticks[j].label.xy.y
});
}
});
});
};
document.getElementById("button").addEventListener("click", function() {
update();
rotate();
}, false);
Live demo: https://jsfiddle.net/BlackLabel/mg5bv3s8/
API Reference: https://api.highcharts.com/class-reference/Highcharts.SVGElement#animate

Center image doc.addImage jspdf

I am using html2canvas to take screenshot of my page and creating pdf of the images using jspdf. Now, my images are left aligned in the pdf document, I want it to be centered, how can I achieve it?
function pdfmaker() {
var element = $("#timesheet");
document.getElementById("message").style.display = "block";
document.getElementById("logo").style.display = "block";
var firstName = "<?php echo $fname?>";
var lastName = "<?php echo $lname ?>";
var startDate = "<?php echo $startDate?>";
var endDate = "<?php echo $endDate ?>";
html2canvas(element, {
useCORS: true,
onrendered: function(canvas) {
var imgData = canvas.toDataURL("image/png");
var imgWidth = 297; //297
var pageHeight = 297; //297
var imgHeight = canvas.height * imgWidth / canvas.width;
var heightLeft = imgHeight;
// var doc = new jsPDF('l', 'mm',[1350, 1350]);
var doc = new jsPDF('l', 'mm', [420, 297]); //420,297
var position = 5; //0
margins = {
top: 20,
bottom: 10,
left: 45,
width: 522
};
doc.addImage(imgData, 'PNG', 5, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 5) {
position = heightLeft - imgHeight;
doc.addPage();
doc.addImage(imgData, 'PNG', 5, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
doc.save(firstName + '_' + lastName + '_Summary_report_' + startDate + '_' + endDate + ".pdf");
}
});
document.getElementById("message").style.display = "none";
document.getElementById("logo").style.display = "none";
}
You need to define inside the addImage() method, using the coordinate parameters, see: http://raw.githack.com/MrRio/jsPDF/master/docs/module-addImage.html
This is the only way you can do it. For this, I suggest you use the following method doc.internal.pageSize.getWidth(); to calculate the excess values ​​about the image width, which will be centered.

KineticJS simple animation not working on mobile devices

I was wondering if someone could help me find the solution to this.
I've made a very simple animation using KineticJS.
All works perfect on desktop, unfortunately not on mobile devices (iPhone, iPad, Android).
Result is a slowish performance but most importantly distorted shapes.
I suspect it has something to do with resolution or viewport but am not sure.
Preview is on www.bartvanhelsdingen.com
Any suggestions are highly appreciated.
Below is the code:
var shapes = {
sizes: [30, 40, 50, 55, 60, 80],
gradients: [
[0, '#fdfaee', 1, '#524f43'],
[0, '#a39175', 1, '#dbae5e'],
[0, '#b4c188', 1, '#f3de7c'],
[0, '#eaf2ef', 1, '#587c71'],
[0, '#a39175', 1, '#dbae5e'],
[0, '#61845c', 1, '#b4b092']
],
},
dims = {
width: 300,
height: 500
},
stage = new Kinetic.Stage({
container: 'animation',
width: dims.width,
height: dims.height,
x: 0,
y: 0,
draggable: false
});
function getRandomColor() {
return colors[getRandomFromInterval(0, colors.length - 1)];
}
function getRandomGradient() {
return gradients[getRandomFromInterval(0, gradients.length - 1)];
}
function getRandomFromInterval(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
function getRandomSpeed() {
var speed = getRandomFromInterval(1, 1);
return getRandomFromInterval(0, 1) ? speed : speed * -1;
}
function createGroup(x, y, size, strokeWidth) {
return new Kinetic.Group({
x: x,
y: y,
width: size,
height: size,
opacity: 0,
draggable: false,
clipFunc: function (canvas) {
var context = canvas.getContext();
context.beginPath();
context.moveTo(0, 0);
context.lineTo(0, size);
context.lineTo(size, size);
context.lineTo(size, 0);
context.rect(strokeWidth, strokeWidth, size - strokeWidth * 2, size - strokeWidth * 2);
}
});
}
function createShape(size, gradient, strokeWidth, cornerRadius) {
return new Kinetic.Rect({
x: 0,
y: 0,
width: size,
height: size,
fillLinearGradientStartPoint: [size, 0],
fillLinearGradientEndPoint: [size, size],
fillLinearGradientColorStops: gradient,
opacity: 1,
lineJoin: 'bevel',
strokeWidth: 0,
cornerRadius: cornerRadius
});
}
var layer = new Kinetic.Layer(),
animAttribs = [];
for (var n = 0; n < 6; n++) {
var size = shapes.sizes[n],
strokeWidth = Math.ceil(size * 0.12),
cornerRadius = Math.ceil(size * 0.04),
gradient = shapes.gradients[n],
x = getRandomFromInterval(size, dims.width) - size,
y = getRandomFromInterval(size, dims.height) - size;
var group = createGroup(x, y, size, strokeWidth);
var shape = createShape(size, gradient, strokeWidth, cornerRadius);
animAttribs.push({
nextChange: getRandomFromInterval(1, 3) * 1000,
startTime: 1000,
duration: 0,
x: getRandomSpeed(),
y: getRandomSpeed()
});
group.add(shape);
layer.add(group);
}
stage.add(layer);
anim = new Kinetic.Animation(function (frame) {
var time = frame.time,
timeDiff = frame.timeDiff,
frameRate = frame.frameRate;
for (var n = 0; n < layer.getChildren().length; n++) {
var shape = layer.getChildren()[n],
opacity = shape.getOpacity() + 0.01 > 1 ? 1 : shape.getOpacity() + 0.01,
attribs = animAttribs[n],
x, y;
if (attribs.duration >= attribs.nextChange) {
attribs.x = getRandomSpeed();
attribs.y = getRandomSpeed();
attribs.nextChange = getRandomFromInterval(3, 5) * 1000;
attribs.duration = 0;
}
if (time >= attribs.startTime) {
if (shape.getX() + attribs.x + shape.getWidth() >= stage.getWidth() || shape.getX() + attribs.x - shape.getWidth() / 2 <= 0) {
attribs.x *= -1;
}
if (shape.getY() + attribs.y + shape.getHeight() >= stage.getHeight() || shape.getY() + attribs.y - shape.getHeight() / 2 <= 0) {
attribs.y *= -1;
}
x = shape.getX() + attribs.x;
y = shape.getY() + attribs.y;
attribs.duration += timeDiff;
shape.setOpacity(opacity);
shape.setX(x);
shape.setY(y);
}
}
}, layer);
anim.start();
the problem you are facing is, that clipFunc isn't currently working on devices with pixelratio != 1.
This problem came up in this post as well. Eric Rowell, the creator of KineticJS added this issue to his release scedule for late September.
So there is nothing wrong with your animations, they're working as expected, but you can't see them because of the distorted clipping region
To resolve this issue "unofficially" you can simply replace the last line of the _clip function in your kinetic.js with the following: context.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); (credits for that go to Mark Smits)

Resources