Scale a Polygon of an image - javascript

I want to be able to scale a polygon of an image on hover.
To be precise, its a world-map, and I want to scale the country bigger, when you hover over it, and then make it smaller again, when not hovering any more.
I know, that there is the transform: scale(2) for CSS for example, and it works fine with normal images or also with world map. But actually I don't want to scale the hole map, but a polygon of the map (or at least, if a polygon is not possible, then a square (so only a little part of the map).
Is there any way to do this? Doesn't have to be CSS only, can also be JS/jQuery.
The hover part is not part of the question, thats simple jQuery, but the question is how to scale only a specific part of an image.
Thanks for any help!

It's been a noisy night here. So I've thrown together something to play with. It's an incomplete implementation - I just grab the part of the image hovered and throw it into a picture along-side the original.
It's up to you to
(0) show the image larger than full size
(1) position it in front of the original, using absolute positioning and z-index
(2) handle mouseout on the hovered area.
The answer I linked to in the comments demonstrates positioning, z-ordering and passing pointer events to the underlying map elements.
You'll need to decide just how/where you wish the enlarged version to appear. Perhaps you'd want it 150%, with the center-point atop the center-point of the map element.
As the comments indicate, you'd normally use an existing image, rather than creating one from scratch, as I've done here.
First, the complete source in a single piece - just copy it all and past into a new html file.
<!DOCTYPE html>
<html>
<head>
<script>
"use strict";
function byId(e){return document.getElementById(e);}
function newEl(tag){return document.createElement(tag);}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(e)
{
createImageFromMapAreasForUseAtStackoverflow();
attachHoverHandlers();
}
// this function is only used for the purpose of answering this question.
// Ordinarily, you would set the src of #img1 to be that of your image. - since cross-origin contamination prevents
// access to the image data if the image comes from a different server, I've just opted to create the image from scratch.
//
// the original image may be found here: https://upload.wikimedia.org/wikipedia/commons/thumb/1/16/Australia_location_map_recolored.png/500px-Australia_location_map_recolored.png
function createImageFromMapAreasForUseAtStackoverflow()
{
var imgWidth = 500, imgHeight = 360;
var can = newEl('canvas');
can.width = imgWidth;
can.height = imgHeight;
var ctx = can.getContext('2d');
ctx.fillStyle = '#ecf5f6';
ctx.fillRect(0,0,imgWidth,imgHeight);
var areas = document.querySelectorAll('area');
var i, n = areas.length;
var colours = ['#f4f100', '#b2d025', '#f67c60', '#8fbce1', '#f4f236', '#fca164', '#bc9eee', 'black'];
for (i=0; i<n; i++)
{
var areaType = areas[i].getAttribute('shape');
if ((areaType == 'polygon') || (areaType == 'poly'))
drawMapPoly(areas[i].getAttribute('coords'), colours[i]);
}
byId('img1').src = can.toDataURL();
function drawMapPoly(coordStr, colour)
{
ctx.beginPath();
var ptArray = coordStrToPointArray(coordStr);
var i, n=ptArray.length;
ctx.moveTo(ptArray[0].x, ptArray[0].y);
for (i=0;i<n;i++)
ctx.lineTo(ptArray[i].x,ptArray[i].y);
ctx.closePath();
ctx.fillStyle=colour;
ctx.fill();
}
}
function attachHoverHandlers()
{
//
// Attach event-listeners to each of the areas in the image-map to handle mouseover
//
var areas = document.querySelectorAll('area');
var i, n = areas.length;
for (i=0; i<n; i++)
{
areas[i].addEventListener('mouseover', onAreaHovered, false);
}
}
function onAreaHovered(e)
{
var hoveredElement = this;
var coordStr = this.getAttribute('coords');
var areaType = this.getAttribute('shape');
switch (areaType)
{
case 'polygon':
case 'poly':
showPolyCoords(coordStr);
break;
default:
alert("You need to add a handler for areas of type '" + areaType + "'");
}
}
function coordStrToPointArray(coordStr)
{
var mCoords = coordStr.split(',');
var i, n = mCoords.length;
var coordArray = [];
coordArray.push( new p2d(mCoords[0], mCoords[1]) );
for (i=2; i<n; i+=2)
{
coordArray.push( new p2d(mCoords[i], mCoords[i+1]) );
}
coordArray.push( new p2d(mCoords[0], mCoords[1]) );
return coordArray;
}
// takes a string that contains coords eg - "227,307,261,309, 339,354, 328,371, 240,331"
// draws a line from each co-ord pair to the next - assumes starting point needs to be repeated as ending point.
function showPolyCoords(coOrdStr)
{
var coordArray = coordStrToPointArray(coOrdStr);
var sortedArray = coordArray.slice();
sortedArray.sort(sortX);
var minX = sortedArray[0].x;
var maxX = sortedArray[sortedArray.length-1].x;
sortedArray.sort(sortY);
var minY = sortedArray[0].y;
var maxY = sortedArray[sortedArray.length-1].y;
var topLeft = new p2d(minX, minY);
var botRight = new p2d(maxX, maxY);
testFuncWithClipping(topLeft, botRight, 'img1', coordArray);
}
function p2d(x, y)
{
this.x = Number(x);
this.y = Number(y);
return this;
}
// unneccesary - just makes displaying the point easier.
// Having this prototype available means that (in chrome at least)
// the code: "console.log( pt2d );" or "alert( pt2d );" will result in "<xCoord, yCoord>" being printed/alerted
p2d.prototype.toString = function()
{
return "<"+this.x+", "+this.y+">";
}
// comparison functions used when sorting the point list to obtain the min/max values of both X and Y
function sortX(a, b){return a.x - b.x;}
function sortY(a, b){return a.y - b.y;}
function testFuncWithClipping(topLeft, botRight, srcImgId, pointArray)
{
var width = botRight.x - topLeft.x;
var height = botRight.y - topLeft.y;
var can = newEl('canvas');
can.width = width;
can.height = height;
var ctx = can.getContext('2d');
var img = byId(srcImgId);
ctx.beginPath();
ctx.moveTo( pointArray[0].x - topLeft.x, pointArray[0].y-topLeft.y );
var i, n = pointArray.length;
for (i=0; i<n; i++)
{
ctx.lineTo( pointArray[i].x - topLeft.x, pointArray[i].y-topLeft.y );
}
ctx.clip();
ctx.drawImage(img, topLeft.x, topLeft.y, width, height, 0,0, width,height);
byId('img2').src = can.toDataURL();
}
</script>
<style>
body
{
background-color: gray;
}
#canvas2
{
pointer-events: none; /* make the canvas transparent to the mouse - needed since canvas is position infront of image */
position: absolute; /* you'll need to use this trick to allow the area to know when the mouse leaves it, so you can hide/destroy the */
} /* enlarged version of the hovered area */
</style>
</head>
<body>
<!--
Usually, you would use this element.
For the purpose of making a working demo, I've used the next element and have created the picture using the map data and
the funtion createImageFromMapAreasForUseAtStackoverflow
As mentioned above, the original image is:
https://upload.wikimedia.org/wikipedia/commons/thumb/1/16/Australia_location_map_recolored.png/500px-Australia_location_map_recolored.png
which was saved as ausMap.png in the img folder of my localhost.
<img id='img1' usemap='#imgMap1' src='img/ausMap.png' />
-->
<img id='img1' usemap='#imgMap1'/>
<map name='imgMap1' id='imgMap1'>
<area shape="polygon" coords="359, 324, 373, 332, 392, 327, 393, 346, 375, 356, 364, 343" title="Tasmania">
<area shape="polygon" coords="325, 258, 335, 258, 339, 265, 346, 265, 347, 271, 357, 279, 360, 279, 361, 276, 368, 278, 380, 279, 388, 277, 390, 288, 406, 293, 405, 296, 391, 297, 377, 308, 376, 310, 372, 308, 356, 303, 350, 307, 338, 303, 332, 301, 325, 300" title="Victoria">
<area shape="polygon" coords="325, 207, 397, 206, 403, 201, 417, 203, 420, 209, 425, 205, 427, 200, 440, 197, 440, 204, 436, 222, 432, 235, 432, 241, 424, 245, 419, 255, 415, 267, 408, 277, 408, 293, 391, 286, 389, 278, 381, 279, 373, 279, 364, 276, 361, 278, 348, 271, 346, 265, 340, 266, 338, 261, 333, 258, 325, 258" title="New South Wales">
<area shape="polygon" coords="325, 206, 398, 207, 404, 201, 417, 203, 423, 207, 427, 201, 434, 198, 441, 198, 436, 169, 429, 159, 419, 150, 416, 142, 410, 138, 406, 139, 400, 121, 397, 114, 375, 101, 372, 82, 367, 79, 365, 61, 357, 52, 353, 56, 349, 39, 345, 31, 341, 22, 336, 18, 336, 27, 330, 35, 334, 38, 331, 41, 330, 46, 331, 60, 329, 66, 329, 74, 326, 77, 326, 85, 320, 90, 312, 88, 308, 82, 298, 78, 298, 175, 325, 175" title="Queensland">
<area shape="polygon" coords="297, 175, 297, 79, 273, 60, 286, 34, 281, 30, 276, 33, 268, 33, 254, 29, 250, 25, 247, 22, 243, 22, 244, 26, 249, 31, 246, 33, 237, 33, 235, 33, 234, 31, 236, 28, 236, 23, 231, 25, 228, 24, 225, 27, 230, 29, 233, 33, 229, 37, 223, 41, 227, 45, 222, 46, 218, 54, 221, 62, 214, 63, 213, 175" title="Northern Territory">
<area shape="polygon" coords="214, 234, 214, 61, 211, 60, 205, 65, 205, 59, 197, 50, 194, 54, 190, 52, 187, 56, 180, 56, 179, 62, 174, 64, 174, 75, 163, 74, 167, 81, 164, 85, 160, 77, 150, 82, 149, 90, 153, 93, 148, 97, 143, 108, 127, 114, 122, 113, 121, 115, 111, 120, 103, 118, 94, 125, 90, 130, 85, 132, 80, 138, 78, 136, 80, 131, 73, 138, 75, 144, 75, 148, 72, 150, 72, 160, 75, 170, 78, 176, 75, 179, 69, 172, 76, 187, 76, 193, 82, 200, 82, 205, 84, 210, 84, 218, 91, 234, 92, 241, 93, 250, 90, 253, 86, 253, 86, 261, 92, 262, 99, 269, 112, 269, 125, 263, 132, 256, 145, 254, 164, 257, 168, 248, 172, 248, 186, 241, 192, 242" title="Western Australia">
<area shape="polygon" coords="324, 299, 323, 175, 213, 175, 213, 234, 233, 232, 242, 238, 249, 236, 261, 242, 258, 246, 265, 249, 269, 256, 270, 261, 272, 263, 277, 267, 277, 261, 281, 257, 288, 254, 291, 249, 295, 246, 295, 243, 297, 250, 294, 254, 290, 259, 291, 265, 287, 269, 294, 268, 297, 262, 301, 268, 299, 272, 295, 275, 290, 273, 285, 274, 283, 277, 290, 278, 294, 275, 301, 273, 315, 286, 314, 291" title="South Australia">
<area shape='rect' coords='0,0,100,100' title='unsupported area type'>
</map>
<img id='img2'/>
</body>
</html>
Next, a working demo that you can try right here on the page (use full-screen for the best experience)
"use strict";
function byId(e){return document.getElementById(e);}
function newEl(tag){return document.createElement(tag);}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(e)
{
createImageFromMapAreasForUseAtStackoverflow();
attachHoverHandlers();
}
// this function is only used for the purpose of answering this question.
// Ordinarily, you would set the src of #img1 to be that of your image. - since cross-origin contamination prevents
// access to the image data if the image comes from a different server, I've just opted to create the image from scratch.
//
// the original image may be found here: https://upload.wikimedia.org/wikipedia/commons/thumb/1/16/Australia_location_map_recolored.png/500px-Australia_location_map_recolored.png
function createImageFromMapAreasForUseAtStackoverflow()
{
var imgWidth = 500, imgHeight = 360;
var can = newEl('canvas');
can.width = imgWidth;
can.height = imgHeight;
var ctx = can.getContext('2d');
ctx.fillStyle = '#ecf5f6';
ctx.fillRect(0,0,imgWidth,imgHeight);
var areas = document.querySelectorAll('area');
var i, n = areas.length;
var colours = ['#f4f100', '#b2d025', '#f67c60', '#8fbce1', '#f4f236', '#fca164', '#bc9eee', 'black'];
for (i=0; i<n; i++)
{
var areaType = areas[i].getAttribute('shape');
if ((areaType == 'polygon') || (areaType == 'poly'))
drawMapPoly(areas[i].getAttribute('coords'), colours[i]);
}
byId('img1').src = can.toDataURL();
function drawMapPoly(coordStr, colour)
{
ctx.beginPath();
var ptArray = coordStrToPointArray(coordStr);
var i, n=ptArray.length;
ctx.moveTo(ptArray[0].x, ptArray[0].y);
for (i=0;i<n;i++)
ctx.lineTo(ptArray[i].x,ptArray[i].y);
ctx.closePath();
ctx.fillStyle=colour;
ctx.fill();
}
}
function attachHoverHandlers()
{
//
// Attach event-listeners to each of the areas in the image-map to handle mouseover
//
var areas = document.querySelectorAll('area');
var i, n = areas.length;
for (i=0; i<n; i++)
{
areas[i].addEventListener('mouseover', onAreaHovered, false);
}
}
function onAreaHovered(e)
{
var hoveredElement = this;
var coordStr = this.getAttribute('coords');
var areaType = this.getAttribute('shape');
switch (areaType)
{
case 'polygon':
case 'poly':
showPolyCoords(coordStr);
break;
default:
alert("You need to add a handler for areas of type '" + areaType + "'");
}
}
function coordStrToPointArray(coordStr)
{
var mCoords = coordStr.split(',');
var i, n = mCoords.length;
var coordArray = [];
coordArray.push( new p2d(mCoords[0], mCoords[1]) );
for (i=2; i<n; i+=2)
{
coordArray.push( new p2d(mCoords[i], mCoords[i+1]) );
}
coordArray.push( new p2d(mCoords[0], mCoords[1]) );
return coordArray;
}
// takes a string that contains coords eg - "227,307,261,309, 339,354, 328,371, 240,331"
// draws a line from each co-ord pair to the next - assumes starting point needs to be repeated as ending point.
function showPolyCoords(coOrdStr)
{
var coordArray = coordStrToPointArray(coOrdStr);
var sortedArray = coordArray.slice();
sortedArray.sort(sortX);
var minX = sortedArray[0].x;
var maxX = sortedArray[sortedArray.length-1].x;
sortedArray.sort(sortY);
var minY = sortedArray[0].y;
var maxY = sortedArray[sortedArray.length-1].y;
var topLeft = new p2d(minX, minY);
var botRight = new p2d(maxX, maxY);
testFuncWithClipping(topLeft, botRight, 'img1', coordArray);
}
function p2d(x, y)
{
this.x = Number(x);
this.y = Number(y);
return this;
}
// unneccesary - just makes displaying the point easier.
// Having this prototype available means that (in chrome at least)
// the code: "console.log( pt2d );" or "alert( pt2d );" will result in "<xCoord, yCoord>" being printed/alerted
p2d.prototype.toString = function()
{
return "<"+this.x+", "+this.y+">";
}
// comparison functions used when sorting the point list to obtain the min/max values of both X and Y
function sortX(a, b){return a.x - b.x;}
function sortY(a, b){return a.y - b.y;}
function testFuncWithClipping(topLeft, botRight, srcImgId, pointArray)
{
var width = botRight.x - topLeft.x;
var height = botRight.y - topLeft.y;
var can = newEl('canvas');
can.width = width;
can.height = height;
var ctx = can.getContext('2d');
var img = byId(srcImgId);
ctx.beginPath();
ctx.moveTo( pointArray[0].x - topLeft.x, pointArray[0].y-topLeft.y );
var i, n = pointArray.length;
for (i=0; i<n; i++)
{
ctx.lineTo( pointArray[i].x - topLeft.x, pointArray[i].y-topLeft.y );
}
// comment the below line to see the effect of drawing a rectangular
// portion of the image without clipping.
ctx.clip();
ctx.drawImage(img, topLeft.x, topLeft.y, width, height, 0,0, width,height);
byId('img2').src = can.toDataURL();
}
body
{
background-color: gray;
}
#canvas2
{
pointer-events: none; /* make the canvas transparent to the mouse - needed since canvas is position infront of image */
position: absolute; /* you'll need to use this trick to allow the area to know when the mouse leaves it, so you can hide/destroy the */
} /* enlarged version of the hovered area */
<!--
Usually, you would use this element.
For the purpose of making a working demo, I've used the next element and have created the picture using the map data and
the funtion createImageFromMapAreasForUseAtStackoverflow
As mentioned above, the original image is:
https://upload.wikimedia.org/wikipedia/commons/thumb/1/16/Australia_location_map_recolored.png/500px-Australia_location_map_recolored.png
which was saved as ausMap.png in the img folder of my localhost.
<img id='img1' usemap='#imgMap1' src='img/ausMap.png' />
-->
<img id='img1' usemap='#imgMap1'/>
<map name='imgMap1' id='imgMap1'>
<area shape="polygon" coords="359, 324, 373, 332, 392, 327, 393, 346, 375, 356, 364, 343" title="Tasmania">
<area shape="polygon" coords="325, 258, 335, 258, 339, 265, 346, 265, 347, 271, 357, 279, 360, 279, 361, 276, 368, 278, 380, 279, 388, 277, 390, 288, 406, 293, 405, 296, 391, 297, 377, 308, 376, 310, 372, 308, 356, 303, 350, 307, 338, 303, 332, 301, 325, 300" title="Victoria">
<area shape="polygon" coords="325, 207, 397, 206, 403, 201, 417, 203, 420, 209, 425, 205, 427, 200, 440, 197, 440, 204, 436, 222, 432, 235, 432, 241, 424, 245, 419, 255, 415, 267, 408, 277, 408, 293, 391, 286, 389, 278, 381, 279, 373, 279, 364, 276, 361, 278, 348, 271, 346, 265, 340, 266, 338, 261, 333, 258, 325, 258" title="New South Wales">
<area shape="polygon" coords="325, 206, 398, 207, 404, 201, 417, 203, 423, 207, 427, 201, 434, 198, 441, 198, 436, 169, 429, 159, 419, 150, 416, 142, 410, 138, 406, 139, 400, 121, 397, 114, 375, 101, 372, 82, 367, 79, 365, 61, 357, 52, 353, 56, 349, 39, 345, 31, 341, 22, 336, 18, 336, 27, 330, 35, 334, 38, 331, 41, 330, 46, 331, 60, 329, 66, 329, 74, 326, 77, 326, 85, 320, 90, 312, 88, 308, 82, 298, 78, 298, 175, 325, 175" title="Queensland">
<area shape="polygon" coords="297, 175, 297, 79, 273, 60, 286, 34, 281, 30, 276, 33, 268, 33, 254, 29, 250, 25, 247, 22, 243, 22, 244, 26, 249, 31, 246, 33, 237, 33, 235, 33, 234, 31, 236, 28, 236, 23, 231, 25, 228, 24, 225, 27, 230, 29, 233, 33, 229, 37, 223, 41, 227, 45, 222, 46, 218, 54, 221, 62, 214, 63, 213, 175" title="Northern Territory">
<area shape="polygon" coords="214, 234, 214, 61, 211, 60, 205, 65, 205, 59, 197, 50, 194, 54, 190, 52, 187, 56, 180, 56, 179, 62, 174, 64, 174, 75, 163, 74, 167, 81, 164, 85, 160, 77, 150, 82, 149, 90, 153, 93, 148, 97, 143, 108, 127, 114, 122, 113, 121, 115, 111, 120, 103, 118, 94, 125, 90, 130, 85, 132, 80, 138, 78, 136, 80, 131, 73, 138, 75, 144, 75, 148, 72, 150, 72, 160, 75, 170, 78, 176, 75, 179, 69, 172, 76, 187, 76, 193, 82, 200, 82, 205, 84, 210, 84, 218, 91, 234, 92, 241, 93, 250, 90, 253, 86, 253, 86, 261, 92, 262, 99, 269, 112, 269, 125, 263, 132, 256, 145, 254, 164, 257, 168, 248, 172, 248, 186, 241, 192, 242" title="Western Australia">
<area shape="polygon" coords="324, 299, 323, 175, 213, 175, 213, 234, 233, 232, 242, 238, 249, 236, 261, 242, 258, 246, 265, 249, 269, 256, 270, 261, 272, 263, 277, 267, 277, 261, 281, 257, 288, 254, 291, 249, 295, 246, 295, 243, 297, 250, 294, 254, 290, 259, 291, 265, 287, 269, 294, 268, 297, 262, 301, 268, 299, 272, 295, 275, 290, 273, 285, 274, 283, 277, 290, 278, 294, 275, 301, 273, 315, 286, 314, 291" title="South Australia">
<area shape='rect' coords='0,0,100,100' title='unsupported area type'>
</map>
<img id='img2'/>
Have fun!

I would honestly make every country its own image and rescale that on hover. Image effects aren't very well supported in JS/jQuery/CSS and if you don't want multiple images then I would start looking for a library to do that for you.
For instance you might want to consider using Pieces
Another possibility would be to use canvas or svg to draw your map and make it interactive using JS.

Related

Attempted import error: 'SupportedPackages' is not exported from '#tensorflow-models/facemesh' (imported as 'facemesh')

I am implementing Real-Time AI Face Landmark Detection
and I am getting this error if anyone has a fix then please help me my code snippet is below in reactjs and also i installed all dependencies too and imported in correct way but still this 'SupportedPackages' is not exported from '#tensorflow-models/facemesh' (imported as 'facemesh') import error is coming
App.js
import React, { useRef, useEffect } from "react";
import "./App.css";
import * as tf from "#tensorflow/tfjs";
import * as facemesh from "#tensorflow-models/face-landmarks-detection";
import Webcam from "react-webcam";
import { drawMesh } from "./utilities";
//setup references
function App() {
const webcamRef = useRef(null);
const canvasRef = useRef(null);
// Load posenet
const runFacemesh = async () => {
// OLD MODEL
// const net = await facemesh.load({
// inputResolution: { width: 640, height: 480 },
// scale: 0.8,
// });
// NEW MODEL
const net = await facemesh.load(facemesh.SupportedPackages.mediapipeFacemesh);
setInterval(() => {
detect(net);
}, 10);
};
const detect = async (net) => {
if (
typeof webcamRef.current !== "undefined" &&
webcamRef.current !== null &&
webcamRef.current.video.readyState === 4
) {
// Get Video Properties
const video = webcamRef.current.video;
const videoWidth = webcamRef.current.video.videoWidth;
const videoHeight = webcamRef.current.video.videoHeight;
// Set video width
webcamRef.current.video.width = videoWidth;
webcamRef.current.video.height = videoHeight;
// Set canvas width
canvasRef.current.width = videoWidth;
canvasRef.current.height = videoHeight;
// Make Detections
// OLD MODEL
// const face = await net.estimateFaces(video);
// NEW MODEL
const face = await net.estimateFaces({input:video});
console.log(face);
// Get canvas context
const ctx = canvasRef.current.getContext("2d");
requestAnimationFrame(()=>{drawMesh(face, ctx)});
}
};
useEffect(()=>{runFacemesh()}, []);
return (
<div className="App">
<header className="App-header">
<Webcam
ref={webcamRef}
style={{
position: "absolute",
marginLeft: "auto",
marginRight: "auto",
left: 0,
right: 0,
textAlign: "center",
zindex: 9,
width: 640,
height: 480,
}}
/>
<canvas
ref={canvasRef}
style={{
position: "absolute",
marginLeft: "auto",
marginRight: "auto",
left: 0,
right: 0,
textAlign: "center",
zindex: 9,
width: 640,
height: 480,
}}
/>
</header>
</div>
);
}
export default App;
all dependencies install stuff is done
and also below error
I got the same issue, and the problem was with the npm package version of #tensorflow-models/face-landmarks-detection & #tensorflow/tfjs
I had to modify the original code.
import { useRef, useEffect } from "react";
import * as facemesh from "#tensorflow-models/face-landmarks-detection";
import Webcam from "react-webcam";
import { drawMesh } from "./utilities";
import "./App.css";
function App() {
const webcamRef = useRef(null);
const canvasRef = useRef(null);
const runFaceMesh = async () => {
const model = facemesh.SupportedModels.MediaPipeFaceMesh;
const detectorConfig = {
runtime: "tfjs",
solutionPath: "https://cdn.jsdelivr.net/npm/#mediapipe/face_mesh",
};
const detector = await facemesh.createDetector(model, detectorConfig);
setInterval(() => {
detect(detector);
}, 10);
};
const detect = async (detector) => {
if (
typeof webcamRef.current !== "undefined" &&
webcamRef.current !== null &&
webcamRef.current.video.readyState === 4
) {
const video = webcamRef.current.video;
const videoWidth = webcamRef.current.video.videoWidth;
const videoHeight = webcamRef.current.video.videoHeight;
webcamRef.current.video.width = videoWidth;
webcamRef.current.video.height = videoHeight;
canvasRef.current.width = videoWidth;
canvasRef.current.height = videoHeight;
const face = await detector.estimateFaces(video);
const ctx = canvasRef.current.getContext("2d");
requestAnimationFrame(() => {
drawMesh(face, ctx);
});
}
};
useEffect(() => {
runFaceMesh();
}, []);
return (
<div className="App">
<header className="App-header">
<Webcam
ref={webcamRef}
style={{
position: "absolute",
marginLeft: "auto",
marginRight: "auto",
left: 0,
right: 0,
textAlign: "center",
zIndex: 9,
width: 640,
height: 480,
}}
/>
<canvas
ref={canvasRef}
style={{
position: "absolute",
marginLeft: "auto",
marginRight: "auto",
left: 0,
right: 0,
textAlign: "center",
zIndex: 9,
width: 640,
height: 480,
}}
/>
</header>
</div>
);
}
export default App;
// utilities.js
export const TRIANGULATION = [
127, 34, 139, 11, 0, 37, 232, 231, 120, 72, 37, 39, 128, 121, 47, 232, 121,
128, 104, 69, 67, 175, 171, 148, 157, 154, 155, 118, 50, 101, 73, 39, 40, 9,
151, 108, 48, 115, 131, 194, 204, 211, 74, 40, 185, 80, 42, 183, 40, 92, 186,
230, 229, 118, 202, 212, 214, 83, 18, 17, 76, 61, 146, 160, 29, 30, 56, 157,
173, 106, 204, 194, 135, 214, 192, 203, 165, 98, 21, 71, 68, 51, 45, 4, 144,
24, 23, 77, 146, 91, 205, 50, 187, 201, 200, 18, 91, 106, 182, 90, 91, 181,
85, 84, 17, 206, 203, 36, 148, 171, 140, 92, 40, 39, 193, 189, 244, 159, 158,
28, 247, 246, 161, 236, 3, 196, 54, 68, 104, 193, 168, 8, 117, 228, 31, 189,
193, 55, 98, 97, 99, 126, 47, 100, 166, 79, 218, 155, 154, 26, 209, 49, 131,
135, 136, 150, 47, 126, 217, 223, 52, 53, 45, 51, 134, 211, 170, 140, 67, 69,
108, 43, 106, 91, 230, 119, 120, 226, 130, 247, 63, 53, 52, 238, 20, 242, 46,
70, 156, 78, 62, 96, 46, 53, 63, 143, 34, 227, 173, 155, 133, 123, 117, 111,
44, 125, 19, 236, 134, 51, 216, 206, 205, 154, 153, 22, 39, 37, 167, 200, 201,
208, 36, 142, 100, 57, 212, 202, 20, 60, 99, 28, 158, 157, 35, 226, 113, 160,
159, 27, 204, 202, 210, 113, 225, 46, 43, 202, 204, 62, 76, 77, 137, 123, 116,
41, 38, 72, 203, 129, 142, 64, 98, 240, 49, 102, 64, 41, 73, 74, 212, 216,
207, 42, 74, 184, 169, 170, 211, 170, 149, 176, 105, 66, 69, 122, 6, 168, 123,
147, 187, 96, 77, 90, 65, 55, 107, 89, 90, 180, 101, 100, 120, 63, 105, 104,
93, 137, 227, 15, 86, 85, 129, 102, 49, 14, 87, 86, 55, 8, 9, 100, 47, 121,
145, 23, 22, 88, 89, 179, 6, 122, 196, 88, 95, 96, 138, 172, 136, 215, 58,
172, 115, 48, 219, 42, 80, 81, 195, 3, 51, 43, 146, 61, 171, 175, 199, 81, 82,
38, 53, 46, 225, 144, 163, 110, 246, 33, 7, 52, 65, 66, 229, 228, 117, 34,
127, 234, 107, 108, 69, 109, 108, 151, 48, 64, 235, 62, 78, 191, 129, 209,
126, 111, 35, 143, 163, 161, 246, 117, 123, 50, 222, 65, 52, 19, 125, 141,
221, 55, 65, 3, 195, 197, 25, 7, 33, 220, 237, 44, 70, 71, 139, 122, 193, 245,
247, 130, 33, 71, 21, 162, 153, 158, 159, 170, 169, 150, 188, 174, 196, 216,
186, 92, 144, 160, 161, 2, 97, 167, 141, 125, 241, 164, 167, 37, 72, 38, 12,
145, 159, 160, 38, 82, 13, 63, 68, 71, 226, 35, 111, 158, 153, 154, 101, 50,
205, 206, 92, 165, 209, 198, 217, 165, 167, 97, 220, 115, 218, 133, 112, 243,
239, 238, 241, 214, 135, 169, 190, 173, 133, 171, 208, 32, 125, 44, 237, 86,
87, 178, 85, 86, 179, 84, 85, 180, 83, 84, 181, 201, 83, 182, 137, 93, 132,
76, 62, 183, 61, 76, 184, 57, 61, 185, 212, 57, 186, 214, 207, 187, 34, 143,
156, 79, 239, 237, 123, 137, 177, 44, 1, 4, 201, 194, 32, 64, 102, 129, 213,
215, 138, 59, 166, 219, 242, 99, 97, 2, 94, 141, 75, 59, 235, 24, 110, 228,
25, 130, 226, 23, 24, 229, 22, 23, 230, 26, 22, 231, 112, 26, 232, 189, 190,
243, 221, 56, 190, 28, 56, 221, 27, 28, 222, 29, 27, 223, 30, 29, 224, 247,
30, 225, 238, 79, 20, 166, 59, 75, 60, 75, 240, 147, 177, 215, 20, 79, 166,
187, 147, 213, 112, 233, 244, 233, 128, 245, 128, 114, 188, 114, 217, 174,
131, 115, 220, 217, 198, 236, 198, 131, 134, 177, 132, 58, 143, 35, 124, 110,
163, 7, 228, 110, 25, 356, 389, 368, 11, 302, 267, 452, 350, 349, 302, 303,
269, 357, 343, 277, 452, 453, 357, 333, 332, 297, 175, 152, 377, 384, 398,
382, 347, 348, 330, 303, 304, 270, 9, 336, 337, 278, 279, 360, 418, 262, 431,
304, 408, 409, 310, 415, 407, 270, 409, 410, 450, 348, 347, 422, 430, 434,
313, 314, 17, 306, 307, 375, 387, 388, 260, 286, 414, 398, 335, 406, 418, 364,
367, 416, 423, 358, 327, 251, 284, 298, 281, 5, 4, 373, 374, 253, 307, 320,
321, 425, 427, 411, 421, 313, 18, 321, 405, 406, 320, 404, 405, 315, 16, 17,
426, 425, 266, 377, 400, 369, 322, 391, 269, 417, 465, 464, 386, 257, 258,
466, 260, 388, 456, 399, 419, 284, 332, 333, 417, 285, 8, 346, 340, 261, 413,
441, 285, 327, 460, 328, 355, 371, 329, 392, 439, 438, 382, 341, 256, 429,
420, 360, 364, 394, 379, 277, 343, 437, 443, 444, 283, 275, 440, 363, 431,
262, 369, 297, 338, 337, 273, 375, 321, 450, 451, 349, 446, 342, 467, 293,
334, 282, 458, 461, 462, 276, 353, 383, 308, 324, 325, 276, 300, 293, 372,
345, 447, 382, 398, 362, 352, 345, 340, 274, 1, 19, 456, 248, 281, 436, 427,
425, 381, 256, 252, 269, 391, 393, 200, 199, 428, 266, 330, 329, 287, 273,
422, 250, 462, 328, 258, 286, 384, 265, 353, 342, 387, 259, 257, 424, 431,
430, 342, 353, 276, 273, 335, 424, 292, 325, 307, 366, 447, 345, 271, 303,
302, 423, 266, 371, 294, 455, 460, 279, 278, 294, 271, 272, 304, 432, 434,
427, 272, 407, 408, 394, 430, 431, 395, 369, 400, 334, 333, 299, 351, 417,
168, 352, 280, 411, 325, 319, 320, 295, 296, 336, 319, 403, 404, 330, 348,
349, 293, 298, 333, 323, 454, 447, 15, 16, 315, 358, 429, 279, 14, 15, 316,
285, 336, 9, 329, 349, 350, 374, 380, 252, 318, 402, 403, 6, 197, 419, 318,
319, 325, 367, 364, 365, 435, 367, 397, 344, 438, 439, 272, 271, 311, 195, 5,
281, 273, 287, 291, 396, 428, 199, 311, 271, 268, 283, 444, 445, 373, 254,
339, 263, 466, 249, 282, 334, 296, 449, 347, 346, 264, 447, 454, 336, 296,
299, 338, 10, 151, 278, 439, 455, 292, 407, 415, 358, 371, 355, 340, 345, 372,
390, 249, 466, 346, 347, 280, 442, 443, 282, 19, 94, 370, 441, 442, 295, 248,
419, 197, 263, 255, 359, 440, 275, 274, 300, 383, 368, 351, 412, 465, 263,
467, 466, 301, 368, 389, 380, 374, 386, 395, 378, 379, 412, 351, 419, 436,
426, 322, 373, 390, 388, 2, 164, 393, 370, 462, 461, 164, 0, 267, 302, 11, 12,
374, 373, 387, 268, 12, 13, 293, 300, 301, 446, 261, 340, 385, 384, 381, 330,
266, 425, 426, 423, 391, 429, 355, 437, 391, 327, 326, 440, 457, 438, 341,
382, 362, 459, 457, 461, 434, 430, 394, 414, 463, 362, 396, 369, 262, 354,
461, 457, 316, 403, 402, 315, 404, 403, 314, 405, 404, 313, 406, 405, 421,
418, 406, 366, 401, 361, 306, 408, 407, 291, 409, 408, 287, 410, 409, 432,
436, 410, 434, 416, 411, 264, 368, 383, 309, 438, 457, 352, 376, 401, 274,
275, 4, 421, 428, 262, 294, 327, 358, 433, 416, 367, 289, 455, 439, 462, 370,
326, 2, 326, 370, 305, 460, 455, 254, 449, 448, 255, 261, 446, 253, 450, 449,
252, 451, 450, 256, 452, 451, 341, 453, 452, 413, 464, 463, 441, 413, 414,
258, 442, 441, 257, 443, 442, 259, 444, 443, 260, 445, 444, 467, 342, 445,
459, 458, 250, 289, 392, 290, 290, 328, 460, 376, 433, 435, 250, 290, 392,
411, 416, 433, 341, 463, 464, 453, 464, 465, 357, 465, 412, 343, 412, 399,
360, 363, 440, 437, 399, 456, 420, 456, 363, 401, 435, 288, 372, 383, 353,
339, 255, 249, 448, 261, 255, 133, 243, 190, 133, 155, 112, 33, 246, 247, 33,
130, 25, 398, 384, 286, 362, 398, 414, 362, 463, 341, 263, 359, 467, 263, 249,
255, 466, 467, 260, 75, 60, 166, 238, 239, 79, 162, 127, 139, 72, 11, 37, 121,
232, 120, 73, 72, 39, 114, 128, 47, 233, 232, 128, 103, 104, 67, 152, 175,
148, 173, 157, 155, 119, 118, 101, 74, 73, 40, 107, 9, 108, 49, 48, 131, 32,
194, 211, 184, 74, 185, 191, 80, 183, 185, 40, 186, 119, 230, 118, 210, 202,
214, 84, 83, 17, 77, 76, 146, 161, 160, 30, 190, 56, 173, 182, 106, 194, 138,
135, 192, 129, 203, 98, 54, 21, 68, 5, 51, 4, 145, 144, 23, 90, 77, 91, 207,
205, 187, 83, 201, 18, 181, 91, 182, 180, 90, 181, 16, 85, 17, 205, 206, 36,
176, 148, 140, 165, 92, 39, 245, 193, 244, 27, 159, 28, 30, 247, 161, 174,
236, 196, 103, 54, 104, 55, 193, 8, 111, 117, 31, 221, 189, 55, 240, 98, 99,
142, 126, 100, 219, 166, 218, 112, 155, 26, 198, 209, 131, 169, 135, 150, 114,
47, 217, 224, 223, 53, 220, 45, 134, 32, 211, 140, 109, 67, 108, 146, 43, 91,
231, 230, 120, 113, 226, 247, 105, 63, 52, 241, 238, 242, 124, 46, 156, 95,
78, 96, 70, 46, 63, 116, 143, 227, 116, 123, 111, 1, 44, 19, 3, 236, 51, 207,
216, 205, 26, 154, 22, 165, 39, 167, 199, 200, 208, 101, 36, 100, 43, 57, 202,
242, 20, 99, 56, 28, 157, 124, 35, 113, 29, 160, 27, 211, 204, 210, 124, 113,
46, 106, 43, 204, 96, 62, 77, 227, 137, 116, 73, 41, 72, 36, 203, 142, 235,
64, 240, 48, 49, 64, 42, 41, 74, 214, 212, 207, 183, 42, 184, 210, 169, 211,
140, 170, 176, 104, 105, 69, 193, 122, 168, 50, 123, 187, 89, 96, 90, 66, 65,
107, 179, 89, 180, 119, 101, 120, 68, 63, 104, 234, 93, 227, 16, 15, 85, 209,
129, 49, 15, 14, 86, 107, 55, 9, 120, 100, 121, 153, 145, 22, 178, 88, 179,
197, 6, 196, 89, 88, 96, 135, 138, 136, 138, 215, 172, 218, 115, 219, 41, 42,
81, 5, 195, 51, 57, 43, 61, 208, 171, 199, 41, 81, 38, 224, 53, 225, 24, 144,
110, 105, 52, 66, 118, 229, 117, 227, 34, 234, 66, 107, 69, 10, 109, 151, 219,
48, 235, 183, 62, 191, 142, 129, 126, 116, 111, 143, 7, 163, 246, 118, 117,
50, 223, 222, 52, 94, 19, 141, 222, 221, 65, 196, 3, 197, 45, 220, 44, 156,
70, 139, 188, 122, 245, 139, 71, 162, 145, 153, 159, 149, 170, 150, 122, 188,
196, 206, 216, 92, 163, 144, 161, 164, 2, 167, 242, 141, 241, 0, 164, 37, 11,
72, 12, 144, 145, 160, 12, 38, 13, 70, 63, 71, 31, 226, 111, 157, 158, 154,
36, 101, 205, 203, 206, 165, 126, 209, 217, 98, 165, 97, 237, 220, 218, 237,
239, 241, 210, 214, 169, 140, 171, 32, 241, 125, 237, 179, 86, 178, 180, 85,
179, 181, 84, 180, 182, 83, 181, 194, 201, 182, 177, 137, 132, 184, 76, 183,
185, 61, 184, 186, 57, 185, 216, 212, 186, 192, 214, 187, 139, 34, 156, 218,
79, 237, 147, 123, 177, 45, 44, 4, 208, 201, 32, 98, 64, 129, 192, 213, 138,
235, 59, 219, 141, 242, 97, 97, 2, 141, 240, 75, 235, 229, 24, 228, 31, 25,
226, 230, 23, 229, 231, 22, 230, 232, 26, 231, 233, 112, 232, 244, 189, 243,
189, 221, 190, 222, 28, 221, 223, 27, 222, 224, 29, 223, 225, 30, 224, 113,
247, 225, 99, 60, 240, 213, 147, 215, 60, 20, 166, 192, 187, 213, 243, 112,
244, 244, 233, 245, 245, 128, 188, 188, 114, 174, 134, 131, 220, 174, 217,
236, 236, 198, 134, 215, 177, 58, 156, 143, 124, 25, 110, 7, 31, 228, 25, 264,
356, 368, 0, 11, 267, 451, 452, 349, 267, 302, 269, 350, 357, 277, 350, 452,
357, 299, 333, 297, 396, 175, 377, 381, 384, 382, 280, 347, 330, 269, 303,
270, 151, 9, 337, 344, 278, 360, 424, 418, 431, 270, 304, 409, 272, 310, 407,
322, 270, 410, 449, 450, 347, 432, 422, 434, 18, 313, 17, 291, 306, 375, 259,
387, 260, 424, 335, 418, 434, 364, 416, 391, 423, 327, 301, 251, 298, 275,
281, 4, 254, 373, 253, 375, 307, 321, 280, 425, 411, 200, 421, 18, 335, 321,
406, 321, 320, 405, 314, 315, 17, 423, 426, 266, 396, 377, 369, 270, 322, 269,
413, 417, 464, 385, 386, 258, 248, 456, 419, 298, 284, 333, 168, 417, 8, 448,
346, 261, 417, 413, 285, 326, 327, 328, 277, 355, 329, 309, 392, 438, 381,
382, 256, 279, 429, 360, 365, 364, 379, 355, 277, 437, 282, 443, 283, 281,
275, 363, 395, 431, 369, 299, 297, 337, 335, 273, 321, 348, 450, 349, 359,
446, 467, 283, 293, 282, 250, 458, 462, 300, 276, 383, 292, 308, 325, 283,
276, 293, 264, 372, 447, 346, 352, 340, 354, 274, 19, 363, 456, 281, 426, 436,
425, 380, 381, 252, 267, 269, 393, 421, 200, 428, 371, 266, 329, 432, 287,
422, 290, 250, 328, 385, 258, 384, 446, 265, 342, 386, 387, 257, 422, 424,
430, 445, 342, 276, 422, 273, 424, 306, 292, 307, 352, 366, 345, 268, 271,
302, 358, 423, 371, 327, 294, 460, 331, 279, 294, 303, 271, 304, 436, 432,
427, 304, 272, 408, 395, 394, 431, 378, 395, 400, 296, 334, 299, 6, 351, 168,
376, 352, 411, 307, 325, 320, 285, 295, 336, 320, 319, 404, 329, 330, 349,
334, 293, 333, 366, 323, 447, 316, 15, 315, 331, 358, 279, 317, 14, 316, 8,
285, 9, 277, 329, 350, 253, 374, 252, 319, 318, 403, 351, 6, 419, 324, 318,
325, 397, 367, 365, 288, 435, 397, 278, 344, 439, 310, 272, 311, 248, 195,
281, 375, 273, 291, 175, 396, 199, 312, 311, 268, 276, 283, 445, 390, 373,
339, 295, 282, 296, 448, 449, 346, 356, 264, 454, 337, 336, 299, 337, 338,
151, 294, 278, 455, 308, 292, 415, 429, 358, 355, 265, 340, 372, 388, 390,
466, 352, 346, 280, 295, 442, 282, 354, 19, 370, 285, 441, 295, 195, 248, 197,
457, 440, 274, 301, 300, 368, 417, 351, 465, 251, 301, 389, 385, 380, 386,
394, 395, 379, 399, 412, 419, 410, 436, 322, 387, 373, 388, 326, 2, 393, 354,
370, 461, 393, 164, 267, 268, 302, 12, 386, 374, 387, 312, 268, 13, 298, 293,
301, 265, 446, 340, 380, 385, 381, 280, 330, 425, 322, 426, 391, 420, 429,
437, 393, 391, 326, 344, 440, 438, 458, 459, 461, 364, 434, 394, 428, 396,
262, 274, 354, 457, 317, 316, 402, 316, 315, 403, 315, 314, 404, 314, 313,
405, 313, 421, 406, 323, 366, 361, 292, 306, 407, 306, 291, 408, 291, 287,
409, 287, 432, 410, 427, 434, 411, 372, 264, 383, 459, 309, 457, 366, 352,
401, 1, 274, 4, 418, 421, 262, 331, 294, 358, 435, 433, 367, 392, 289, 439,
328, 462, 326, 94, 2, 370, 289, 305, 455, 339, 254, 448, 359, 255, 446, 254,
253, 449, 253, 252, 450, 252, 256, 451, 256, 341, 452, 414, 413, 463, 286,
441, 414, 286, 258, 441, 258, 257, 442, 257, 259, 443, 259, 260, 444, 260,
467, 445, 309, 459, 250, 305, 289, 290, 305, 290, 460, 401, 376, 435, 309,
250, 392, 376, 411, 433, 453, 341, 464, 357, 453, 465, 343, 357, 412, 437,
343, 399, 344, 360, 440, 420, 437, 456, 360, 420, 363, 361, 401, 288, 265,
372, 353, 390, 339, 249, 339, 448, 255,
];
const drawPath = (ctx, points, closePath) => {
const region = new Path2D();
region.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length; i++) {
const point = points[i];
region.lineTo(point.x, point.y);
}
if (closePath) {
region.closePath();
}
ctx.strokeStyle = "grey";
ctx.stroke(region);
};
export const drawMesh = (predictions, ctx) => {
if (predictions.length > 0) {
predictions.forEach((prediction) => {
const keyPoints = prediction.keypoints;
for (let i = 0; i < TRIANGULATION.length / 3; i++) {
const points = [
TRIANGULATION[i * 3],
TRIANGULATION[i * 3 + 1],
TRIANGULATION[i * 3 + 2],
].map((index) => keyPoints[index]);
drawPath(ctx, points, true);
}
for (let i = 0; i < keyPoints.length; i++) {
const x = keyPoints[i].x;
const y = keyPoints[i].y;
ctx.beginPath();
ctx.arc(x, y, 1, 0, 3 * Math.PI);
ctx.fillStyle = "aqua";
ctx.fill();
}
});
}
};

Zoom is very slow in ChartJS Zoom Plugin

I'm trying to build a webpage with some graphs. In order to get it, I'm using ChartJS. Sometimes can happen there are too many data to see in just one canvas, so I need to use the ChartJS zoom plugin. I install and use it well, unless I have a big problem:
Setting the mode as 'x', the zoom applied on canvas is really really slow (even if I put 100000 as speed value). I want to make it faster. How can I solve it? This is my code:
new Chart(document.getElementById('like-growth-lt'), {
type: 'line',
data: {
labels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515],
datasets: [{
data: [64, 44, 71, 71, 24, 32, 54, 56, 36, 37, 50, 38, 48, 33, 46, 88, 43, 45, 48, 36, 44, 38, 64, 63, 89, 40, 50, 85, 79, 69, 64, 70, 66, 70, 40, 178, 57, 25, 38, 105, 79, 58, 89, 102, 94, 87, 91, 93, 100, 72, 78, 65, 68, 123, 79, 107, 91, 66, 89, 90, 45, 127, 138, 79, 58, 81, 92, 97, 52, 97, 43, 64, 81, 62, 69, 74, 75, 54, 93, 43, 101, 83, 49, 106, 77, 86, 105, 91, 73, 129, 99, 64, 104, 92, 105, 141, 96, 100, 84, 101, 75, 100, 76, 112, 171, 94, 78, 97, 87, 100, 198, 112, 98, 95, 127, 107, 74, 122, 137, 45, 147, 147, 169, 125, 147, 129, 118, 164, 90, 135, 132, 135, 135, 239, 197, 165, 261, 150, 164, 238, 271, 196, 307, 246, 137, 112, 142, 265, 295, 173, 172, 116, 127, 141, 142, 134, 196, 298, 305, 273, 121, 99, 108, 138, 191, 310, 375, 360, 427, 271, 191, 168, 123, 323, 70, 126, 138, 222, 170, 160, 185, 236, 160, 290, 192, 196, 386, 166, 186, 236, 232, 425, 181, 142, 103, 126, 176, 292, 200, 186, 179, 277, 305, 610, 140, 200, 283, 218, 384, 183, 165, 156, 195, 215, 210, 157, 337, 208, 799, 130, 242, 80, 337, 181, 261, 307, 264, 371, 639, 379, 253, 384, 141, 391, 212, 1371, 321, 149, 345, 345, 159, 337, 313, 1131, 204, 339, 396, 208, 285, 99, 440, 410, 187, 138, 109, 241, 318, 225, 415, 350, 231, 267, 432, 195, 228, 418, 360, 334, 204, 706, 270, 70, 328, 219, 319, 594, 168, 229, 632, 281, 259, 391, 150, 628, 273, 339, 432, 237, 667, 319, 302, 88, 167, 334, 274, 121, 303, 636, 233, 1399, 465, 404, 211, 209, 537, 361, 192, 189, 474, 1694, 659, 297, 495, 883, 417, 427, 298, 438, 251, 381, 422, 442, 225, 418, 177, 476, 292, 619, 147, 293, 375, 228, 326, 539, 306, 416, 317, 239, 1095, 605, 219, 392, 320, 296, 216, 355, 563, 291, 792, 298, 319, 252, 526, 225, 225, 399, 387, 255, 311, 199, 364, 542, 405, 260, 175, 173, 582, 231, 201, 196, 222, 909, 265, 191, 243, 346, 349, 163, 263, 151, 217, 261, 172, 191, 387, 314, 379, 172, 157, 446, 120, 131, 494, 445, 294, 414, 485, 407, 415, 214, 242, 159, 168, 145, 256, 123, 350, 999, 296, 171, 790, 1051, 620, 330, 231, 1742, 194, 547, 360, 136, 503, 555, 646, 580, 609, 566, 256, 181, 189, 562, 176, 622, 758, 692, 358, 202, 1877, 1157, 668, 311, 1037, 736, 1477, 616, 677, 605, 224, 838, 306, 704, 503, 448, 1198, 778, 171, 921, 1071, 1099, 837, 329, 2337, 395, 1063, 297, 1415, 1195, 745, 341, 931, 1178, 336, 1016, 234, 632, 573, 495, 944, 993, 1066, 1980, 619, 556, 238, 818, 1003, 532, 1427, 373, 249, 304, 790, 2962, 985, 252, 1545, 276, 1219, 1112, 1109, 1367, 792, 1116, 1055, 648, 740, 435, 1528, 805, 565, 1183, 1105, 1498, 1453, 381, 651, 2073],
label: '',
borderColor: 'rgb(22, 133, 204)',
backgroundColor: 'rgba(22, 133, 204, 0.3)',
},
]
},
options: {
pan: {
enabled: true,
mode: "x",
speed: 10000000000000000
},
zoom: {
enabled: true,
drag: false,
mode: "x",
speed: 10000000000000000
},
responsive: true
}
});
.panel{
position: relative;
padding-bottom: 20px;
width: 100%;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3"></script>
<script src="https://cdn.jsdelivr.net/npm/hammerjs#2.0.8"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom#0.7.7"></script>
<div class="panel" id="panel-lt">
<canvas id="like-growth-lt" ></canvas>
</div>
You need to set your scales.xAxes.type to numeric and then set the speed to 0.1. Speed is from 0 to 1, so setting it to 1000000 is useless.

Efficient way of finding the 5 largest numbers in an array at any given interval

I am trying to find the five largest numbers at set intervals, while also Removing those values from the array. I need to grab the top candidates in their respective range. That range can change and the number that I need to query can also change. Is there an efficient and preferably elegant solution to this? By elegance, I mean an algorithmic (preferably hashed) approach that removes inefficient sorting or actions that do not contribute to performance on sparse and large arrays.
var arr = [101, 88, 267, 175, 154, 39, 74, 217, 31, 105, 235, 31, 14, 49, 226, 195, 134, 207, 222, 281,
262, 112, 133, 115, 0, 53, 128, 103, 88, 145, 238, 13, 204, 199, 100, 247, 292, 157, 141, 286,
72, 160, 85, 61, 57, 54, 263, 50, 125, 179, 243, 281, 39, 76, 151, 79, 1, 238, 200, 249, 35, 82,
204, 174, 293, 216, 84, 209, 170, 236, 3, 247, 25, 162, 25, 57, 49, 215, 8, 167, 180, 268,
204, 257, 134, 151, 191, 81, 77, 106, 85, 128, 52, 136, 46, 185, 229, 116, 145, 253, 258, 222,
269, 225, 101, 175, 265, 77, 32, 8, 72, 54, 111, 264, 292, 161, 91, 215, 139, 245, 73, 127, 297,
73, 258, 183, 232, 55, 199, 175, 31, 24, 21, 155, 231, 95, 40, 223, 222, 86, 115, 210, 134, 229,
211, 54, 294, 153, 52, 165, 168, 125,186, 185, 289, 188, 248, 61, 136, 15, 19, 92, 200, 80, 208,
195, 241, 85, 288, 279, 119, 247, 208, 11, 80, 111, 29, 292, 222, 289, 70, 11, 209, 25, 267, 233,
16, 289, 154, 141, 174, 30, 156, 40, 266, 139, 116, 241, 1, 101, 109, 61, 220, 265, 45, 178, 166,
102, 181, 193, 202, 133, 200, 266, 114, 222, 231, 89, 190, 29, 20, 64, 233, 261,213, 40, 161, 167,
100, 121, 288, 268, 50, 264, 78, 105, 21, 33, 79, 114, 5, 134, 56, 259, 124, 44, 134, 133, 74, 176,
65, 68, 34, 56, 2, 287, 63, 167, 299, 59, 290, 241, 104, 75, 76, 116, 225, 297, 208, 136, 265, 290,
170, 267, 10, 176, 141, 217, 195, 4, 173, 32, 150, 271, 238, 171, 195, 16, 282, 77, 62, 39, 44, 248,
270, 222, 295, 122, 190, 230];
function maxAtIntervals (intervalLength, select, xs) {
const comparator = (a, b, _) => a - b;
const temp = [];
for (var i = 0; i < xs.length; i += intervalLength) {
const interval = xs.slice(i, i + intervalLength);
temp.push(interval.sort(comparator).slice(-select));
}
return temp;
}
console.log(maxAtIntervals(20, 5, arr));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have read #le_m's comment however finding the k largest / smallest items or the kth largest / smallest item is a complicated task in O(n). It's best implemented in sorting and taking the necessary ones from the beginning of the array.
Accordingly you may do as follows;
function segmentAndTakeMax(ar,sl,mc) { // array , segment length, max count
var tempar = Array.from({length: sl});
return Array.from({length: Math.ceil(ar.length/sl)})
.map((_,i) => tempar.map((_,j) => arr[i*sl+j])
.sort((a,b) => b-a)
.slice(0,Math.min(arr.length-i*sl,mc)));
}
var arr = Array.from(new Array(203), _ => ~~(Math.random()*100));
console.log(arr);
console.log(segmentAndTakeMax(arr,20,5));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ok as per OP's performance concerns on V8 i have repharsed the code to use .reduce() which is much faster than .map() in V8. Here is the modified code.
function segmentAndTakeMax(arr, n, m) {
var li = arr.length-1; // last index
return arr.reduce((r,e,i,a) => i%n ? (r[r.length-1].push(e), // if i%n != 0 then do these -> push e to last sub array
i == li && (r[r.length-1] = r[r.length-1].sort((a,b) => b-a).slice(0,m)), // short circuit for if i == last index then sort and slice the last sub array
r) // return r
: (i && (r[r.length-1] = r[r.length-1].sort((a,b) => b-a).slice(0,m)), // if i%n == 0 then do these -> short circuit for if i != 0 then sort and slice the last sub array
r.push([e]), // push [e] (a new sub array) to r
r), []); // return r
}
var arr = Array.from(new Array(203), _ => ~~(Math.random()*100));
console.log(arr);
console.log(segmentAndTakeMax(arr,20,5));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Reading image.data from canvas-Element: Data is different for same image

I have a question about images in a canvas.
HTML1:
I try to create an image in an canvas (HTML1). After I create the image (in this case some random pixels), I replace my canvas with an image (toDataURL()) and save the image using right-click and "save as"-browser function. I save it as "Download.png" in the same directory as HTML2-file.
At the end of my drawImage-function, I make an console.log(img.data); for debugging the image-data (important for my question!).
HTML2:
I load the image (Download.png) from HTML1 into my canvas and try to get the image-data again (ctx.getImageData(...)). Now, the data is different from the data in HTML1, but it is the same image.
For example:
Output HTML1:
[47, 42, 27, 49, 45, 54, 45, 57, 56, 48, 48, 32, 48, 48, 43, 48, 48, 43, 48, 48, 49, 49, 25, 42, 38, 11, 0, 47, 41, 29, 46, 106, 111, 98, 94, 115, 101, 41, 104, 81, 116, 97, 114, 121, 46, 105, 109, 117, 91, 108, 119, 45, 39, 39, 50, 46, 49, 44, 107, 105, 109, 43, 102, 115, 41, 29, 46, 106, 111, 98, 94, 115, 100, 44, 83, 105, 115, 112, 47, 87, 101, 96, 47, 104, 101, 104, 109, 101, 111, 43, 102, 115, 41, 29, 46, 106, 111, 98, 94, 115…]
Output HTML2:
[211, 150, 67, 46, 5, 156, 196, 103, 250, 61, 235, 217, 131, 19, 71, 162, 160, 117, 115, 242, 190, 157, 33, 39, 0, 39, 59, 26, 226, 66, 45, 62, 55, 87, 47, 223, 216, 50, 213, 97, 227, 47, 214, 81, 163, 215, 176, 240, 25, 16, 151, 113, 131, 179, 158, 224, 147, 209, 200, 237, 239, 178, 136, 222, 80, 240, 169, 236, 123, 52, 143, 89, 8, 92, 66, 194, 64, 32, 69, 48, 38, 143, 53, 68, 213, 221, 99, 186, 172, 117, 193, 168, 0, 0, 0, 0, 79, 157, 255, 237…]
Why? Can somebody explain this to me? Why is the image.data different? As far as I have understood it, it should return the same rgba-data as in HTML1-example. How can I fix this behaviour? I want to have exactly the same data.
Thank you in advance!
I have created following testcase and I hope everybody will understand my problem.
HTML1:
<canvas id="canvas" height="100" width="100"></canvas>
<script type="text/javascript">
(function(){
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext("2d");
drawImage(ctx);
replace_canvas_with_img();
})();
function drawImage(ctx) {
var color = [],
img = ctx.createImageData(canvas.width,canvas.height);
for (var i=img.data.length;i--;) {
//generate random color (rgba)
for (var c = 4; c--;) {
color[c] = ''+Math.floor(Math.random() * (255 - 0 + 1));
}
//add random color to image.data;
for (var ci = color.length; ci--;) {
img.data[i+ci] = color[ci];
}
color.length = 0;
}
ctx.putImageData(img, 0, 0);
console.log(img.data);
}
function replace_canvas_with_img() {
document.write('<img src="' + canvas.toDataURL('image/png', 1) + '" />');
canvas.parentNode.removeChild(canvas);
}
</script>
HTML2:
<canvas id="canvas" height="100" width="100"></canvas>
<script type="text/javascript">
(function(){
var myImg = new Image();
myImg.src = 'Download.png';
myImg.onload = function() {
var canvas = document.getElementById('canvas'),
ctx;
canvas.width = myImg.width;
canvas.height = myImg.height;
ctx = canvas.getContext("2d");
ctx.drawImage(this, 0,0);
var imgData = ctx.getImageData(0,0,canvas.width, canvas.height);
console.log(imgData.data);
}
})();
</script>
The large differences in the two array's in our questions are probably user error. HTML1 was probably refreshed before you compared with the Downloaded png.
You will see small differences in the two array's because of the lossy nature of putPixelData.
See:
This SO question and
this or the
HTML5 Canvas Spec
You can see the small differences clearly if you put the pixel get and get the pixel data in the same HTML file. I.e. add this line to HTML1 after your first console.log statement.
console.log(ctx.getImageData(0,0,canvas.width, canvas.height).data);

multidimensional array

How do i create from the following code ?
var x = [12, 155, 177];
var y = [120, 175, 255];
var z = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300];
var value = [];
for (var i = 0; i < x.length; i++) {
for (var index = x[i]; index < y[i]; index++) {
value.push(z[index]);
}
}
console.log(value);
this code generate a one dimensional array from 12 to 12 , 155 to 175 and 177 to 255
RESULT : [13 ,.....,120 , 155 ,.......,175 , 177 ,.........,255]
what i want to do is create another array in this array to get
[[13....120] , [155,.....,175] , [175,.....,255]] ;
jsfiddle : http://jsfiddle.net/minagabriel/SK2vJ/
THANKS
Try this
var x = [12, 155, 177];
var y = [120, 175, 255];
var z = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300];
var value = [];
for (var i = 0; i < x.length; i++) {
var temp = []
for (var index = x[i]; index < y[i]; index++) {
temp.push(z[index]);
}
value.push(temp);
}
console.log(value);
I'm not quite following exactly what you want to do, but here's some info which should help.
JavaScript doesn't have multi-dimensional arrays. (In fact, JavaScript arrays aren't really arrays at all.) But you can have an array which has arrays as element values. So:
var i, j, a, sub;
// Create the main array
a = [];
// This loop creates the outermost level
for (i = 0; i < 10; ++i) {
// Here we create the array to store in a[i]:
sub = [];
// This loop builds it up
for (j = 0; j < 20; ++j) {
sub[j] = "I'm the element at a[" + i + "][" + j + "]"; // Just a sample value
}
// Remember this sub-array
a[i] = sub;
}
console.log("a[1][12] = " + a[1][12]); // "I'm the element at a[1][12]"
Key notes on the above:
You have to create each of the subordinate arrays in the loop
You don't have to pre-allocate or anything like that, you can extend an array just by assigning to an element. (push also works. It's a teeny bit slower, but that usually doesn't matter.)
Something like this should work. I also used a for loop to generate the z array instead of writing it out manually.
var x = [12, 155, 177], y = [120, 175, 255], z = [], value = [];
// generate z, 1 to 300
for(var i = 1; i<=300; i++) {
z.push(i);
}
for(var i = 0, len = x.length; i < len; i++) {
var arr = [];
for(var j = 0; j < y[i]; j++) {
arr.push(z[j]);
}
value.push(arr);
}

Categories

Resources