Offset when drawing on canvas - javascript

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/

Related

can't enable scroll bar on css style overflow: scroll on <canva>s or container div

I am trying to make a long drawing canvas, but users can exit drawing mode and go to Hand scroll mode just like you normally scroll website on your phone.
I was thinking to add a scrollbar on the side of the canvas. But it would be better if we can scroll the canvas normally like we reading a website on phone.
html,css code:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no,initial-scale=1.0,maximum-scale=1.0" />
<style>
.container {
margin: 10px;
overflow: scroll;
}
#main {border: 1px solid #0000ff; overflow: scroll;}
</style>
<script src="draw.js"></script>
</head>
<body>
<div class="container">
<button id="clear">clear button that will clear teh canvas</button><br>
<canvas id="main" width="250" height="1000"></canvas>
</div>
</body>
</html>
javascript code for touch drawing:
window.onload = function() {
document.ontouchmove = function(e){ e.preventDefault(); }
var canvas = document.querySelector('#main');
// const width = canvas.width = window.innerWidth;
// const height = canvas.height = window.innerHeight;
var canvastop = canvas.offsetTop;
var context = canvas.getContext("2d");
var lastx;
var lasty;
context.strokeStyle = "#000000";
context.lineCap = 'round';
context.lineJoin = 'round';
context.lineWidth = 5;
function clear() {
context.fillStyle = "#ffffff";
context.rect(0, 0, 300, 300);
context.fill();
}
function dot(x,y) {
context.beginPath();
context.fillStyle = "#000000";
context.arc(x,y,1,0,Math.PI*2,true);
context.fill();
context.stroke();
context.closePath();
}
function line(fromx,fromy, tox,toy) {
context.beginPath();
context.moveTo(fromx, fromy);
context.lineTo(tox, toy);
context.stroke();
context.closePath();
}
canvas.ontouchstart = function(event){
event.preventDefault();
lastx = event.touches[0].clientX;
lasty = event.touches[0].clientY - canvastop;
dot(lastx,lasty);
}
canvas.ontouchmove = function(event){
event.preventDefault();
var newx = event.touches[0].clientX;
var newy = event.touches[0].clientY - canvastop;
line(lastx,lasty, newx,newy);
lastx = newx;
lasty = newy;
}
var clearButton = document.getElementById('clear')
clearButton.onclick = clear
clear()
}
I don't really understand what you'd like to do. However no need to add 'scroll' to everything.
Try this:
.container {
margin: 10px;
}
#main {
border: 1px solid #0000ff;
width: 250px;
height:600px;
overflow-y: auto
}
And then:
<canvas id="main"></canvas>
In this way your canvas will get a vertical scroll only if its contents is more than 600px, and at the same time you'll be able to scroll the page in a normal way.

How to draw freely in a canvas in html 5 which is overlay above video?

I have a video element and canvas element. The styles are in the following way:
canvas {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height:100%;
width:100%;
background-color: rgba(0,0,255,0.5);
z-index:10;
}
video {
position: absolute;
top: 0;
left: 0;
}
The HTML is as follows
<video id="local-video" class="ui large image hidden" autoplay></video>
<canvas id="c1" class="ui large"></canvas>
I am trying to draw in the canvas over the video element. When I set the position of canvas to fixed, I can draw in canvas with below code, but canvas is not overlayed in the video. But when I set position to absolute, the canvas is overlayed in the video but I can't draw in the video. I checked my console for the points of context.moveTo() and context.lineTo() and they are showing perfectly but are not drawn in the canvas. Please help.
//canvas
var canvas = document.getElementById('c1');
var context= canvas.getContext('2d');
var localVideo = document.getElementById('local-video')
localVideo.addEventListener( "loadedmetadata", function (e) {
var width = this.videoWidth,
height = this.videoHeight;
canvas.height = height;
canvas.width = width;
}, false );
$('#c1').mousedown(function(e){
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
paint = true;
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop,false);
redraw();
});
$('#c1').mousemove(function(e){
if(paint){
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
redraw();
}
});
$('#c1').mouseup(function(e){
paint = false;
});
$('#c1').mouseleave(function(e){
paint = false;
});
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
function addClick(x, y, dragging)
{
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
function redraw(){
context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas
context.strokeStyle = "rgba(0,0,255,0)";
context.lineJoin = "round";
context.lineWidth = 5;
for(var i=0; i < clickX.length; i++) {
context.beginPath();
if(clickDrag[i] && i){
context.moveTo(clickX[i-1], clickY[i-1]);
}else{
context.moveTo(clickX[i]-1, clickY[i]);
}
context.lineTo(clickX[i], clickY[i]);
context.stroke();
context.closePath();
}
}
I don't know if this is hat you are looking for: I'm using a video and a canvas. I can click to start and to stop the video and I can draw freely on the canvas.
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 400,
cx = cw / 2;
let ch = canvas.height = 200,
cy = ch / 2;
ctx.strokeStyle = "#fff";
let drawing = false;
// a function to detect the mouse position
function oMousePos(element, evt) {
var ClientRect = element.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
parentDiv.addEventListener('mousedown', function(evt) {
drawing = true; // you can draw now
let m = oMousePos(parentDiv, evt);
ctx.beginPath();
ctx.moveTo(m.x,m.y);
}, false);
parentDiv.addEventListener('mouseup', function(evt) {
drawing = false; // you can't draw anymore
}, false);
parentDiv.addEventListener('mouseleave', function(evt) {
drawing = false; // you can't draw anymore
}, false);
parentDiv.addEventListener("mousemove", function(evt) {
if (drawing) {
let m = oMousePos(parentDiv, evt);
ctx.lineTo(m.x, m.y);
ctx.stroke();
}
}, false);
body {
background-color: #222;
overflow: hidden;
}
video,canvas {
width:400px;
height:200px;
display: block;
position:absolute;
}
canvas {pointer-events:none;}
<div id="parentDiv">
<video width="400" controls>
<source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4">
<source src="https://www.w3schools.com/html/mov_bbb.ogg" type="video/ogg">
Your browser does not support HTML5 video.
</video>
<canvas id="canvas"></canvas>
</div>
<p>
Video courtesy of
Big Buck Bunny.
</p>
I'm not using jQuery but I hope you'll get the idea.
I rewrote the script in plain js, leaving HTML and CSS like yours and now it works
var oldX, oldY;
var draw=false;
canvas.onmousedown=function(e) {
oldX = (e.pageX - this.offsetLeft)/4;
oldY = (e.pageY - this.offsetTop)/4;
draw=true;
}
canvas.onmousemove=function(e) {
var mouseX = (e.pageX - this.offsetLeft)/4; //was out of scale, this gets it almost
var mouseY = (e.pageY - this.offsetTop)/4; // where I want it to be, to fix
if(draw) {
context.beginPath();
context.moveTo(oldX, oldY);
context.lineTo(mouseX, mouseY);
// console.log(mouseX, mouseY);
context.stroke();
context.closePath();
oldX=mouseX;
oldY=mouseY;
}
}
canvas.onmouseup=function(e) {
draw=false;
}

Image not showing in javascript

When running my program I have an image I want to draw on the mouse's x position(clientX) and y position(clientY).
When running the program regularly by not using client x and y in the position for being drawn, it works just fine.
This is the image
//variables
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var player = new Image();
player.src = "http://i.stack.imgur.com/P9vJm.png";
//functions
function start() {
setInterval(update, 10);
}
function update() {
clearRender();
render();
}
function clearRender() {
ctx.clearRect(0,0,1300,500);
}
function render() {
var mx = document.clientX;
var my = document.clientY;
ctx.drawImage(player, mx, my);
}
#canvas {
height: 500px;
width: 1300px;
background-color: black;
position: absolute;
left: 25px;
top: 50px;
}
body {
background-color: white;
}
<!DOCTYPE html>
<html>
<head>
<title> Space Invaders </title>
<link rel="stylesheet" type="text/css" href="invader.css">
</head>
<body>
<canvas id="canvas"> </canvas>
<script type="text/javascript" src="invader.js"></script>
</body>
</html>
To get you started, you would need an event handler to get your mouse position
c.addEventListener('mousemove', update, false);
JS:
//variables
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var player = new Image();
player.src = "http://i.stack.imgur.com/P9vJm.png";
c.addEventListener('mousemove', update, false);
//functions
function start() {
setInterval(update, 10);
}
function update(e) {
//clearRender();
render(e);
}
function clearRender() {
ctx.clearRect(0, 0, 1300, 500);
}
function render(e) {
var pos = getMousePos(c, e);
posx = pos.x;
posy = pos.y;
ctx.drawImage(player, posx, posy);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
Here's a fiddle: https://jsfiddle.net/vwbo8k6k/
This might help you understand more about mouse positions, but I hope the image shows this time.
http://stackoverflow.com/a/17130415/2036808

javascript console in the browser went unresponsive when running my script

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>

Canvas Fill Page Width and Height

http://jsbin.com/oMUtePo/1/edit
I've been having a pain trying to get canvas to fill the whole page.
I tried using...
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
It filled the width, but not the height, and the drawing board was drawing 1px lines, which is not what I want.
When using 100% for width and height in CSS the width is scaled, and the height is cut, when drawing it looks as if a raster image was scaled significantly larger in ms paint and there's a large offset on onmousedown drawing which is obviously not what I want.
Any help would be greatly appreciated.
Full Code:
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>HTML5 Canvas Drawing Board</title>
<style>
* {
margin: 0;
padding: 0;
}
body, html {
height: 100%;
}
#myCanvas {
cursor: crosshair;
position: absolute;
width: 100%;
height: 100%;
}
</style>
<script type="text/JavaScript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js?ver=1.4.2"></script>
<script type="text/javascript">
window.onload = function() {
var myCanvas = document.getElementById("myCanvas");
var curColor = $('#selectColor option:selected').val();
var ctx = myCanvas.getContext("2d");
ctx.fillStyle="#000";
ctx.fillRect(0,0,500,500);
if(myCanvas){
var isDown = false;
var canvasX, canvasY;
ctx.lineWidth = 5;
$(myCanvas)
.mousedown(function(e){
isDown = true;
ctx.beginPath();
canvasX = e.pageX - myCanvas.offsetLeft;
canvasY = e.pageY - myCanvas.offsetTop;
ctx.moveTo(canvasX, canvasY);
})
.mousemove(function(e){
if(isDown !== false) {
canvasX = e.pageX - myCanvas.offsetLeft;
canvasY = e.pageY - myCanvas.offsetTop;
ctx.lineTo(canvasX, canvasY);
ctx.strokeStyle = "white";
ctx.stroke();
}
})
.mouseup(function(e){
isDown = false;
ctx.closePath();
});
}
};
</script>
</head>
<body>
<canvas id="myCanvas">
Sorry, your browser does not support HTML5 canvas technology.
</canvas>
</body>
</html>
You have to set the absolute size in pixels on the canvas element and not with CSS as you do in the demo, so first remove the following lines from the CSS rule:
#myCanvas {
cursor: crosshair;
position: absolute;
/*width: 100%; Remove these */
/*height: 100%;*/
}
Then add this to your code - you need to use clientWidth/Height of the window object.
myCanvas.width = window.innerWidth;
myCanvas.height = window.innerHeight;
var ctx = myCanvas.getContext("2d");
ctx.fillStyle="#000";
ctx.fillRect(0,0, myCanvas.width, myCanvas.height);
Your modified JSBIN.

Categories

Resources