How to test speed of javascript code - SVG vs CANVAS - html

I have two similar examples but one using SVG other using CANVAS:
WIth html5 canvas: http://jsbin.com/yepigu/5
With SVG: http://jsbin.com/yumova
I need to know which render is faster. What I need to use in my case?
Is there some online tool for that or I need to make some timer into code ?

To perf test the 2 alternatives, run each alternative while recording starting & ending times.
BTW, I reworked your canvas stroked line to work without shadowing which should speed that up:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var pts = [{x:22,y:59.45},{x:136,y:66},{x:170,y:99},{x:171,y:114},{x:183,y:125},{x:218,y:144},{x:218,y:165},{x:226,y:193},{x:254,y:195},{x:283,y:195},{x:292,y:202},{x:325,y:213},{x:341,y:134},{x:397,y:245},{x:417,y:548}];
ctx.lineCap='round';
ctx.lineJoin='round';
ctx.lineWidth=25;
ctx.strokeStyle='red';
drawPolyline(pts);
ctx.lineWidth=22;
ctx.strokeStyle='pink';
drawPolyline(pts);
ctx.lineWidth=2;
ctx.strokeStyle='blue';
drawPolyline(pts);
function drawPolyline(pts){
ctx.beginPath();
ctx.moveTo(pts[0].x,pts[0].y);
for(var i=1;i<pts.length;i++){
ctx.lineTo(pts[i].x,pts[i].y);
}
ctx.stroke();
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=600 height=600></canvas>

Related

Write svg latex into a canvas html

My goal is to record maths scripts running on the canvas and at the same time record sound from the mic (I am a math teacher). I would like very much to allow latex formulas. Of course latex formulas do not write directly to canvas. MathJax can produce SVG elements. I wonder if it is possible to go from SVG->image->canvas, using javascript. I don't care if the obtained image on canvas is a little blured.
I couldn't find good examples of this yet on the internet.
Thanks!
Indeed there is no Latex to canvas direct way.
You can however draw an SVG over a canvas. See this Q/A to see how to proceed from an SVG in the DOM (which MathJax should give you).
Indeed that's possible. The trick here is to grab the SVG output of MathJax and draw it to a temporary <img> element, which in-turn is drawn to an on-screen canvas afterwards.
The actual <svg> element is a children of the <mjx-container> element returned by a call to MathJax's tex2svg() method.
Here's an example:
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let equation = "x = \\sin \\left( \\frac{\\pi}{2} \\right)";
let svg = MathJax.tex2svg(equation).firstElementChild;
let img = document.createElement('img');
img.onload = (e) => {
let tempWidth = e.target.naturalWidth;
let tempHeight = e.target.naturalHeight;
ctx.drawImage(e.target, canvas.width / 2 - tempWidth / 2, canvas.height / 2 - tempHeight / 2, tempWidth, tempHeight)
}
img.src = 'data:image/svg+xml;base64,' + btoa('<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n' + svg.outerHTML);
#canvas {
background-color: #eeeeee;
}
<script src="https://cdn.jsdelivr.net/npm/mathjax#3/es5/tex-svg.js" type="text/javascript"></script>
<canvas id="canvas"></canvas>

Best way to programmatically manipulate an hand-drawn line

Please notice that I have no background on mathematics or computer graphics.
I would like to know the best way to programmaticaly manipulate an hand-drawn line, if it is even possible.
The draw action must be done in a html page. (may be irrelevant)
methods I tough off:
Draw a line into a canvas (hand-drawn line with up and downs) -> convert to bitmap -> somewhow intepret line on bitmap and manipulate its form (is this possible?)
Instead of interpret from bitmap, at the drawing moment have a kind of button to toggle capture on/off and after capture, generate some kind of mathematical function wich I am able to manipulate and from it generate the new bitmap
Yes, you can.
It's not difficult, but there are lots of small coding aspects to learn.
If you’re in “drawing” mode, you would collect mousepoints that the user clicks and make a line from all those points.
If you’re in “editing” mode, you would let the user drag one of those collected points to a new coordinate and make a line from all the edited points.
Here’s starting code for you to look and learn from plus a Fiddle: http://jsfiddle.net/m1erickson/J5PrN/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.lineWidth=3;
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var isDown=false;
var startX;
var startY;
var points=[];
var selected=-1;
var mode="draw";
function draw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.moveTo(points[0].x,points[0].y);
for(var i=1;i<points.length;i++){
ctx.lineTo(points[i].x,points[i].y);
}
ctx.stroke();
if(mode=="edit"){
for(var i=0;i<points.length;i++){
ctx.beginPath();
ctx.arc(points[i].x,points[i].y,10,0,Math.PI*2);
ctx.closePath();
ctx.fill();
}
}
}
function handleMouseDown(e){
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
if(mode=="draw"){
points.push({x:startX,y:startY});
draw();
}else if(mode=="edit"){
for(var i=0;i<points.length;i++){
var pt=points[i];
var dx=startX-pt.x;
var dy=startY-pt.y;
if(dx*dx+dy*dy<100){
selected=i;
return;
}
}
}
}
function handleMouseUp(e){
selected=-1;
}
function handleMouseOut(e){
selected=-1;
}
function handleMouseMove(e){
if(selected<0){return;}
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mousemove stuff here
if(selected<0){return;}
var dx=mouseX-startX;
var dy=mouseY-startY;
startX=mouseX;
startY=mouseY;
var pt=points[selected];
points[selected]={x:pt.x+dx,y:pt.y+dy};
draw();
}
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
$("#draw").click(function(){
mode="draw";
draw();
$instructions.text("Click points to draw a line");
});
$("#edit").click(function(){
mode="edit";
draw();
$instructions.text("Drag points to move the line");
});
$instructions=$("#instructions");
}); // end $(function(){});
</script>
</head>
<body>
<button id="draw">Add to Line</button>
<button id="edit">Change Line</button><br>
<p id="instructions">Click points to draw a line</p>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I can't speak to HTML, but in most applications I have seen (such as this one), hand drawn lines are broken up into small straight segments. This is because the sensing system (touch or mouse) gives your application a (somewhat) continuous stream of points; it does not give the actual line. The individual segments are then used to do whatever the goal of the application is.
In the case of line drawing, as the line is drawn, the application takes the points and smooths them (cubic spline, least squares polynomial fit, b-spline, etc.) then draws the smoothed lines onto the screen in the color and style (pen, pencil, chalk, etc.). This gives the user immediate feedback about where their hand is moving, etc.
In the case of gestural control, some overlay line may be drawn to give the user feedback, but the segments are processed differently to determine the gesture (this can be very complex).
Having the lines cached as a series of gestures gives you options for undo/redo. You can also store the drawing as a series of gestures instead of a fixed bitmap.
Was this helpful?

Grid Zoom - html5 canvas transformations not working

I'm very new to coding. My project is to zoom in to an canvas image based on a 100px square. My selector square is moving around the image successfully with the old squares deleted as I move around the grid.
Here's my code:
HTML
<!doctype HTML>
<html lang="en">
<head>
<script src="draw.js"></script>
</head>
<body>
<section id="main">
<canvas id="canvas" width="400" height="600" style='border:3px solid red'>
</canvas>
</section>
</body>
JAVASCRIPT
function doFirst(){
var x = document.getElementById('canvas')
canvas=x.getContext('2d');
canvas.strokeStyle = "blue";
var pic= new Image();
pic.src = "tut.jpg";
pic.addEventListener ("load",function(){canvas.drawImage(pic,0,0)},false);
}
function select(e){
var xPos=e.clientX;
var yPos=e.clientY;
var locationX = Math.floor(xPos/100)*100
var locationY = Math.floor(yPos/100)*100
var pic= new Image();
pic.src = "tut.jpg";
canvas.drawImage(pic,0,0);
canvas.strokeRect(locationX,locationY,100,100);
}
function zoom (e) {
var locationX = Math.floor(xPos/100)*100
var locationY = Math.floor(yPos/100)*100
canvas.translate(-locationX,-locationY);
canvas.scale(5,5);
}
window.addEventListener ("load", doFirst, false);
window.addEventListener ("mousemove", select, false);
window.addEventListener("mousedown",zoom,false);
The zoom function doesn't kick in at all. And I'm not sure how to move on from there next function - a simple colouring app -= rather than going back to the select function once it has - order functions is still a bit of a mystery. Excuse any naivety as I've probably made some huge clangers in this code.
All help much appreciated,
Nick
To answer your question regarding zoom():
Variables in JavaScript operate under function scope. That is, any variables declared in a function remain visible only to whatever's inside that function.
When you attempt to use xPos and yPos in zoom(), you will get undefined values for them because xPos and yPos are only declared inside select().
xPos and yPos need to be declared and calculated in zoom() as well.
Of course! Thank you.
The zoom function seems a little harder to perfect than I thought: The scale seems to throw the offset out.
Also my animated square refreshed differently on Chrome and IE. How strange. Smooth on IE but flashes on Chrome.
Is there a good place to read up about creating a sequence of function events or do I just put one function inside the other?
Really appreciate your help,
Nick

HTML5 Canvas Scaling using getImageData and putImageData

Is there any way to scale the canvas using getImageData and putImageData.Below is the snippet of code.
var c=document.getElementById("myCanvas");
var c2=document.getElementById("myCanvas2");
var ctx=c.getContext("2d");
var ctx2=c2.getContext("2d");
ctx.fillStyle="red";
ctx.fillRect(10,10,50,50);
function copy(){
var imgData=ctx.getImageData(10,10,50,50);
ctx2.translate(133.333,0);
ctx2.scale(0.75,1);
ctx2.putImageData(imgData,10,70);
}
I have tried this out http://jsbin.com/efixur/1/edit.
Thanks
Ajain
getImageData() and putImageData() are provided for raw pixel operations and thus scaling is not possible (unless you write a custom scaler in Javascript).
What you want is to use drawImage() and then use another <canvas> as source instead of <img>.
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Using_images#Scaling

html5 - canvas element - Multiple layers

Without any extension library, is it possible to have multiple layers in the same canvas element?
So if I do a clearRect on the top layer, it will not erase the bottom one?
Thanks.
No, however, you could layer multiple <canvas> elements on top of each other and accomplish something similar.
<div style="position: relative;">
<canvas id="layer1" width="100" height="100"
style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
<canvas id="layer2" width="100" height="100"
style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
</div>
Draw your first layer on the layer1 canvas, and the second layer on the layer2 canvas. Then when you clearRect on the top layer, whatever's on the lower canvas will show through.
Related to this:
If you have something on your canvas and you want to draw something at the back of it - you can do it by changing the context.globalCompositeOperation setting to 'destination-over' - and then return it to 'source-over' when you're done.
var context = document.getElementById('cvs').getContext('2d');
// Draw a red square
context.fillStyle = 'red';
context.fillRect(50,50,100,100);
// Change the globalCompositeOperation to destination-over so that anything
// that is drawn on to the canvas from this point on is drawn at the back
// of what's already on the canvas
context.globalCompositeOperation = 'destination-over';
// Draw a big yellow rectangle
context.fillStyle = 'yellow';
context.fillRect(0,0,600,250);
// Now return the globalCompositeOperation to source-over and draw a
// blue rectangle
context.globalCompositeOperation = 'source-over';
// Draw a blue rectangle
context.fillStyle = 'blue';
context.fillRect(75,75,100,100);
<canvas id="cvs" />
You can create multiple canvas elements without appending them into document. These will be your layers:
Then do whatever you want with them and at the end just render their content in proper order at destination canvas using drawImage on context.
Example:
/* using canvas from DOM */
var domCanvas = document.getElementById('some-canvas');
var domContext = domCanvas.getContext('2d');
domContext.fillRect(50,50,150,50);
/* virtual canvase 1 - not appended to the DOM */
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'blue';
ctx.fillRect(50,50,150,150);
/* virtual canvase 2 - not appended to the DOM */
var canvas2 = document.createElement('canvas')
var ctx2 = canvas2.getContext('2d');
ctx2.fillStyle = 'yellow';
ctx2.fillRect(50,50,100,50)
/* render virtual canvases on DOM canvas */
domContext.drawImage(canvas, 0, 0, 200, 200);
domContext.drawImage(canvas2, 0, 0, 200, 200);
And here is some codepen: https://codepen.io/anon/pen/mQWMMW
I was having this same problem too, I while multiple canvas elements with position:absolute does the job, if you want to save the output into an image, that's not going to work.
So I went ahead and did a simple layering "system" to code as if each layer had its own code, but it all gets rendered into the same element.
https://github.com/federicojacobi/layeredCanvas
I intend to add extra capabilities, but for now it will do.
You can do multiple functions and call them in order to "fake" layers.
You might also checkout http://www.concretejs.com which is a modern, lightweight, Html5 canvas framework that enables hit detection, layering, and lots of other peripheral things. You can do things like this:
var wrapper = new Concrete.Wrapper({
width: 500,
height: 300,
container: el
});
var layer1 = new Concrete.Layer();
var layer2 = new Concrete.Layer();
wrapper.add(layer1).add(layer2);
// draw stuff
layer1.sceneCanvas.context.fillStyle = 'red';
layer1.sceneCanvas.context.fillRect(0, 0, 100, 100);
// reorder layers
layer1.moveUp();
// destroy a layer
layer1.destroy();
but layer 02, will cover all drawings in layer 01. I used this to show drawing in both layers. use (background-color: transparent;) in style.
<div style="position: relative;">
<canvas id="lay01" width="500" height="500" style="position: absolute; left: 0; top: 0; z-index: 0; background-color: transparent;">
</canvas>
<canvas id="lay02" width="500" height="500" style="position: absolute; left: 0; top: 0; z-index: 1; background-color: transparent;">
</canvas>
</div>
I understand that the Q does not want to use a library, but I will offer this for others coming from Google searches. #EricRowell mentioned a good plugin, but, there is also another plugin you can try, html2canvas.
In our case we are using layered transparent PNG's with z-index as a "product builder" widget. Html2canvas worked brilliantly to boil the stack down without pushing images, nor using complexities, workarounds, and the "non-responsive" canvas itself. We were not able to do this smoothly/sane with the vanilla canvas+JS.
First use z-index on absolute divs to generate layered content within a relative positioned wrapper. Then pipe the wrapper through html2canvas to get a rendered canvas, which you may leave as-is, or output as an image so that a client may save it.