I wrote a simple script to draw a path on HTML5 canvas, however when running the javascript in chrome:
The longer the path grows (as I draw it), the less responsive the page is, and eventually the javascript console is totally stuck, and I noticed the CPU is almost 100% busy. Could you please give me some hints?
Here is my code:
<!DOCTYPE html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script>
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
var context;
window.onload =function(){init();};
function init(){
context = document.getElementById("surface").getContext("2d");
$('#surface').mousedown(function(e){
var touchX = e.pageX - this.offsetLeft;
var touchY = e.pageY - this.offsetTop;
paint = true;
addClick(touchX, touchY);
redraw();
});
$('#surface').mousemove(function(e){
if(paint){
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
redraw();
}
});
$('#surface').mouseup(function(e){
paint = false;
});
$('#surface').mouseleave(function(e){
paint = false;
});
};
function addClick(x, y){
clickX.push(x);
clickY.push(y);
}
function redraw(){
context.strokeStype = "#df4b26";
context.lineJoin = "round";
context.lineWidth = 5;
console.log(clickX.length);
context.beginPath();
for(var i=0; i<clickX.length;i++){
context.lineTo(clickX[i], clickY[i]);
console.log(clickX[i]+", "+clickY[i]);
context.stroke();
}
context.closePath();
}
</script>
</head>
<body>
<canvas id="surface" style="border:1px solid #000000;" width="800" height ="600"></canvas>
</body>
Here are some issues I noticed:
A Demo after refactoring some of your code: http://jsfiddle.net/m1erickson/LStXc/
You have a typo in redraw (strokeStype s/b strokeStyle)
If your project doesn't require saving/reusing the user's polyline then you don't need to save the mouse positions in an array. You can just continuously draw the line by adding a .lineTo with each mousemove event.
Some performance issues:
Calling external functions (as your addClick) are slower than inline commands so consider moving the addClick code into the mousemove handler.
If your canvas is not being repositioned, you can calculate the offsets once before sketching begins.
Setting context state (.strokeStyle, .lineJoin, .lineWidth) are relative expensive when repeated in a very active function like a mouse handler. If your state does not change during the sketch then set these once before sketching begins
Example Code:
<!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("surface");
var context=canvas.getContext("2d");
var canvasOffset=$("#surface").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
context.strokeStyle = "#df4b26";
context.lineJoin = "round";
context.lineWidth = 5;
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
var context;
$('#surface').mousedown(function(e){
var touchX = e.clientX - offsetX;
var touchY = e.clientY - offsetY;
paint = true;
clickX.push(e.clientX-offsetX);
clickY.push(e.clientY-offsetY);
lastX=touchX;
lastY=touchY;
});
$('#surface').mousemove(function(e){
if(paint){
var x=e.clientX-offsetX;
var y=e.clientY-offsetY;
clickX.push(x);
clickY.push(y);
context.beginPath();
context.moveTo(lastX,lastY)
context.lineTo(x,y);
context.stroke();
context.closePath();
lastX=x;
lastY=y;
}
});
$('#surface').mouseup(function(e){
paint = false;
});
$('#surface').mouseleave(function(e){
paint = false;
});
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="surface" width=300 height=300></canvas>
</body>
</html>
You can try with this redraw function:
var actClick = 0;
function redraw(){
context.strokeStype = "#df4b26";
context.lineJoin = "round";
context.lineWidth = 5;
console.log(clickX.length);
context.beginPath();
actClick = actClick - 2;
for( ; actClick < clickX.length ; actClick++ ){
context.lineTo(clickX[actClick], clickY[actClick]);
context.stroke();
}
context.closePath();
}
You can find the jsFiddle
The problem is that you redraw the wole image every new point, so every new point it will become slower.
Try the below code. Hope this will work for you
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script>
var paint;
var context;
window.onload =function(){init();};
function init(){
context = document.getElementById("surface").getContext("2d");
$('#surface').mousedown(function(e){
var touchX = e.pageX; //e.pageX - this.offsetLeft;
var touchY = e.pageY; //e.pageY - this.offsetTop;
paint = true;
context.beginPath();
redraw(touchX, touchY);
});
$('#surface').mousemove(function(e){
if(paint){
var touchX = e.pageX; //e.pageX - this.offsetLeft;
var touchY = e.pageY; //e.pageY - this.offsetTop;
redraw(touchX, touchY);
}
});
$('#surface').mouseup(function(e){
paint = false;
});
$('#surface').mouseleave(function(e){
paint = false;
});
};
function redraw(nextX,nextY){
context.strokeStype = "#df4b26";
context.lineJoin = "round";
context.lineWidth = 5;
console.log(clickX.length);
context.lineTo(nextX, nextY);
console.log(nextX+", "+nextY);
context.stroke();
//context.closePath();
}
</script>
</head>
<body>
<canvas id="surface" style="border:1px solid #000000;" width="800" height ="600"></canvas>
</body>
Related
Im new to canvas and i was playing around with it.
I wanted to draw a circle on mouse click and radius of circle should be decided by mouse drag (similar to drawing circle in Paint in windows )
i have tried creating a circle but struck doing this
my code
<html>
<head>
</head>
<body style="margin:0">
<canvas id="canvas" width="500" height="500" style="border:1px solid"></canvas>
<script>
var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d');
var radius=50;
var putPoint = function(e){
context.beginPath();
context.arc(e.clientX, e.clientY, radius, 0, Math.PI*2);
context.fill();
}
canvas.addEventListener('mousedown',putPoint);
</script>
</body>
</html>
my code in plunker
http://plnkr.co/edit/DPa9km1hHAAsCBCbnGvB?p=preview
I would be happy if someone help me out
Thanks in advance
the general ideia is that you have to take 2 points, the one there circle center will be (this is where the user presses the mouse), and the current point, where the mouse is moving
the distance between the 2 points will be the radius, and is given by the Pythagorean theorem h^2 = c^2 + c^2 so:
onmousedown save that point as center
omousemove calculate the distance to center and use as radius
omouseup stop the operation/animation
here is some sample code, modify to your needs
<html>
<head>
</head>
<body style="margin:0">
<canvas id="canvas" width="500" height="500" style="border:1px solid"></canvas>
<script>
var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d');
var radius=50;
var nStartX = 0;
var nStartY = 0;
var bIsDrawing = false;
var putPoint = function(e){
nStartX = e.clientX;nStartY = e.clientY;
bIsDrawing = true;
radius = 0;
}
var drawPoint = function(e){
if(!bIsDrawing)
return;
var nDeltaX = nStartX - e.clientX;
var nDeltaY = nStartY - e.clientY;
radius = Math.sqrt(nDeltaX * nDeltaX + nDeltaY * nDeltaY)
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(nStartX, nStartY, radius, 0, Math.PI*2);
context.fill();
}
var stopPoint = function(e){
bIsDrawing = false;
}
canvas.addEventListener('mousedown',putPoint);
canvas.addEventListener('mousemove',drawPoint);
canvas.addEventListener('mouseup',stopPoint);
</script>
</body>
</html>
I am trying to draw a rectangle on an image using the mouse and dragging events on HTML5.
My code is shown below. When I draw the rectangle below, the actual image on the canvas disappears. Could you tell me what I am doing wrong? My intended goal is to have the rectangle on top of the image. I have attached a picture of what I actually want to see as the end result.
What am I doing wrong ?
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="400"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
ctx.drawImage(imageObj, 69, 50);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
// ctx.globalAlpha = 0.5;
rect = {},
drag = false;
var rectStartXArray = new Array() ;
var rectStartYArray = new Array() ;
var rectWArray = new Array() ;
var rectHArray = new Array() ;
function init() {
canvas.addEventListener('mousedown', mouseDown, false);
canvas.addEventListener('mouseup', mouseUp, false);
canvas.addEventListener('mousemove', mouseMove, false);
}
function mouseDown(e) {
rect.startX = e.pageX - this.offsetLeft;
rect.startY = e.pageY - this.offsetTop;
drag = true;
}
function mouseUp() {
rectStartXArray[rectStartXArray.length] = rect.startX;
rectStartYArray[rectStartYArray.length] = rect.startY;
rectWArray[rectWArray.length] = rect.w;
rectHArray[rectHArray.length] = rect.h;
drag = false;
}
function mouseMove(e) {
if (drag) {
rect.w = (e.pageX - this.offsetLeft) - rect.startX;
rect.h = (e.pageY - this.offsetTop) - rect.startY;
ctx.clearRect(0, 0, canvas.width, canvas.height);
draw();
}
//drawOldShapes();
}
function draw() {
ctx.beginPath();
ctx.fillStyle="#FF0000";
ctx.fillRect(rect.startX, rect.startY, rect.w, rect.h);
ctx.stroke();
}
function drawOldShapes(){
for(var i=0;i<rectStartXArray.length;i++)
{
if(rectStartXArray[i]!= rect.startX && rectStartYArray[i] != rect.startY && rectWArray[i] != rect.w && rectHArray[i] != rect.h)
{
ctx.beginPath();
ctx.fillStyle="#FF0000";
ctx.fillRect(rectStartXArray[i], rectStartYArray[i], rectWArray[i], rectHArray[i]);
ctx.stroke();
}
}
}
init();
</script>
</body>
</html>
You are clearing the whole canvas inside draw() by calling ctx.fillRect(rect.startX, rect.startY, rect.w, rect.h);. Remove the line and it works. Fiddle - http://jsfiddle.net/da8wv75k/
I currently have a code that simply draws circles onto a canvas. However, I want those circles to be able to direct the user to a given link if he or she chooses to click on the circle again. I'm not entirely sure how to implement this though. Simply put, can drawn objects be used as a click event to direct user to another webpage?
http://jsfiddle.net/PTDy9/
<head>
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
</head>
<body>
<img id="graph" style=display:none src="http://i47.tinypic.com/29zr14o.jpg" alt="graph" >
<canvas id="myCanvas" width="700" height="400" style="border:1px solid #FFFFF;">
<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("graph");
ctx.drawImage(img,0,0);
var color_list = ["#FFC0CB", "#00ffff", "#DA70D6", "#90EE90", "#FF8C00", "#CD853F"];
var color_iter = 0;
var bullet_y = 450;
var width = img.naturalWidth;
var height = img.naturalHeight;
jQuery(document).ready(function(){
$("#myCanvas").click(function(e){
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
if (x < width && y < height) {
var ctx= this.getContext("2d");
ctx.beginPath();
ctx.arc(x, y, 10,0, 2*Math.PI);
color = color_list[color_iter];
ctx.strokeStyle = color;
ctx.fillStyle = color;
if (color_iter == color_list.length-1) {
color_iter = 0;
}
else {color_iter = color_iter + 1;}
ctx.fillStyle = color;
ctx.globalAlpha = .4;
ctx.fill();
ctx.stroke();
var a = document.createElement('a');
}
});
})
</script>
</body>
Here's one way let the user click on a circle to open a specified url in a new browser tab.
First create 1+ link objects specifying the click area on the canvas and the desired url:
var links=[];
addLink(75,75,30,"Google","http://www.google.com");
addLink(150,150,30,"CNN","http://www.cnn.com");
function addLink(x,y,radius,label,url){
links.push({
cx:x,
cy:y,
radius:radius,
label:label,
link:url
});
}
Then listen for mouse clicks and test if any link area was clicked.
If any specific link area was clicked, then open up the corresponding url in a new browser tab:
$("#canvas").mousedown(function(e){handleMouseDown(e);});
function handleMouseDown(e){
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<links.length;i++){
var link=links[i];
var dx=link.cx-mx;
var dy=link.cy-my;
if(dx*dx+dy*dy<link.radius*link.radius){
window.open(link.link,'_blank');
}
}
}
Full Example Code:
<!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");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();
var links=[];
addLink(75,75,30,"Google","http://www.google.com");
addLink(150,150,30,"CNN","http://www.cnn.com");
drawLinks();
$("#canvas").mousedown(function(e){handleMouseDown(e);});
function addLink(x,y,radius,label,url){
links.push({
cx:x,
cy:y,
radius:radius,
label:label,
link:url
});
}
function drawLinks(){
ctx.save();
ctx.fillStyle="green";
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.font="14px verdana";
ctx.textAlign="center";
ctx.textBaseline="middle";
for(var i=0;i<links.length;i++){
var link=links[i];
ctx.beginPath();
ctx.arc(link.cx,link.cy,link.radius,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle="blue";
ctx.fill();
ctx.fillStyle="white";
ctx.fillText(link.label,link.cx,link.cy);
}
ctx.restore();
}
function handleMouseDown(e){
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<links.length;i++){
var link=links[i];
var dx=link.cx-mx;
var dy=link.cy-my;
if(dx*dx+dy*dy<link.radius*link.radius){
window.open(link.link,'_blank');
}
}
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I'm really pretty new to code. I'm trying to make a paint app that will work on desktop and mobile. I had it working fine on desktop using JavaScript but then to get it to work on mobile it seemed like JQuery mobile was a recommended method. I'm converting it to JQuery and had the maint working with .mousedown, .mouseup, etc. but when I changed to to .vmousedown, .vmouseup, etc. to have it work with touch I get an error which I can't seem to resolve.
Uncaught TypeError: Object [object Object] has no method 'vmousedown'
I've seen other people with similar issues but I'm having a hard time making it work for me.
JSFiddle - http://jsfiddle.net/mquickel/dehAD/79/
HTML Snippet
<html>
<head>
<link rel="stylesheet" type="text/css" href="colorCSS2.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>
</head>
<body data-role="page">
<div id="container">
<div id="sketch" data-role="content">
<canvas id="paint" style="z-index: 0; position:absolute;" height="600px" width="600px"></canvas>
<canvas id="layer2" style="z-index: 1; position:absolute;" height="600px" width="600px"></canvas>
</div>
JS Snippet
document.getElementById( "container" ).onmousedown = function(event){
event.preventDefault();
}
var layer2 = document.getElementById("layer2");
var ctx2 = layer2.getContext("2d");
var imageObj = new Image();
/* Loading the Image*/
imageObj.onload = function() {
ctx2.drawImage(imageObj, 0, 0);
};
imageObj.src = 'https://lh5.googleusercontent.com/-P5ucC3TjCLU/UjHE0rENTaI/AAAAAAAAAts/mH2A_OORkQY/s800/color.png';
(function() {
var canvas = document.getElementById('paint');
var ctx = canvas.getContext('2d');
var imageObj = new Image();
var cont = document.getElementById('container');
var mouse = {x: 0, y: 0};
var last_mouse = {x: 0, y: 0};
/* Mouse Capturing Work */
$(cont).mousemove (function(e) {
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop;
});
/* Drawing on Paint App */
ctx.lineWidth = 20;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = brushColor;
$(cont).vmousedown(function(e) {
console.log("hi");
$(cont).vmousemove (onPaint);
});
$(cont).vmouseup (function() {
console.log("up");
$(cont).unbind ('vmousemove', onPaint);
});
var onPaint = function() {
ctx.beginPath();
ctx.moveTo(last_mouse.x, last_mouse.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.closePath();
ctx.stroke();
};
}());
Have a simple drawing application. The problem is in the coordinates (redraw function): they must be mouse, but are near 2x mouse. What's the problem in the code?
<html>
<head>
<style type="text/css" media="screen">
body{
background-color: green;
}
#workspace{
width: 700px;
height:420px;
margin: 40px auto 20px auto;
border: black dashed 1px;
}
#canvas{
background: white;
width: 100%;
height: 100%;
}
</style>
<script type="text/javascript" src="jquery-1.7.1.js"></script>
<script type="text/javascript">
$(document).ready(
function() {
var context = document.getElementById('canvas').getContext("2d");
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
$('#canvas').mousedown(function(e){
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
paint = true;
addClick(mouseX,mouseY, false);
redraw();
});
$('#canvas').mousemove(function(e){
if(paint){
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
redraw();
}
});
$('#canvas').mouseup(function(e){
paint = false;
});
$('#canvas').mouseleave(function(e){
paint = false;
});
function addClick(x, y, dragging)
{
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
function redraw(){
canvas.width = canvas.width; // Clears the canvas
context.strokeStyle = "#df4b26";
context.lineJoin = "round";
context.lineWidth = 5;
for(var i=1; i < clickX.length; i++)
{
context.beginPath();
if(clickDrag[i] && i){
context.moveTo(clickX[i-1], clickY[i-1]);
}else{
context.moveTo(clickX[i], clickY[i]);
}
context.lineTo(clickX[i], clickY[i]);
context.closePath();
context.stroke();
}
console.log(clickX[clickX.length-1]+" "+clickY[clickX.length-1]);
}
});
</script>
</head>
<body>
<div id="workspace">
<canvas id="canvas"/>
</div>
</body>
</html>
You should not set a canvas' width/height through CSS. It makes the canvas stretch instead of making the resolution higher. This means that although the coordinates are correct, they will visually end up somewhere else.
For example, an x coordinate of 100 will be stretched to visually be an x coordinate of 200 (or something else; at least it will be bigger than 100 since it's been stretched).
Instead, use:
<canvas id="canvas" width="700" height="420" />
and remove the width: 100% in CSS.
http://jsfiddle.net/euXJC/1/