Reputation: 1521
I currently try to a dashed line in a canvas.
What I tried is ctx.setLineDash([5,5])
and well, if I draw very fast it works, but that's all. The method seems to not work, when I draw a line very slow. So on my canvas, I can draw myself with the mouse, and I want that drawn line to be dashed. Only when I move my mouse very fast, it works, when I move it slow, I just get a straight through line.
What can I do to get it working, even when drawing slowly?
Code:
<html>
<script type="text/javascript">
var canvas, ctx, flag = false,
prevX = 0,
currX = 0,
prevY = 0,
currY = 0,
dot_flag = false;
var x = "black",
y = 2;
function init() {
canvas = document.getElementById('can');
ctx = canvas.getContext("2d");
ctx.setLineDash([5,5]); //here I try to set the line to a dashed line
w = canvas.width;
h = canvas.height;
canvas.addEventListener("mousemove", function (e) {
findxy('move', e)
}, false);
canvas.addEventListener("mousedown", function (e) {
findxy('down', e)
}, false);
canvas.addEventListener("mouseup", function (e) {
findxy('up', e)
}, false);
canvas.addEventListener("mouseout", function (e) {
findxy('out', e)
}, false);
}
function color(obj) {
switch (obj.id) {
case "green":
x = "green";
break;
case "blue":
x = "blue";
break;
case "red":
x = "red";
break;
case "yellow":
x = "yellow";
break;
case "orange":
x = "orange";
break;
case "black":
x = "black";
break;
case "white":
x = "white";
break;
}
if (x == "white") y = 14;
else y = 2;
}
function draw() {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.stroke();
ctx.closePath();
}
function erase() {
var m = confirm("Want to clear");
if (m) {
ctx.clearRect(0, 0, w, h);
document.getElementById("canvasimg").style.display = "none";
}
}
function save() {
document.getElementById("canvasimg").style.border = "2px solid";
var dataURL = canvas.toDataURL();
document.getElementById("canvasimg").src = dataURL;
document.getElementById("canvasimg").style.display = "inline";
}
function findxy(res, e) {
if (res == 'down') {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
flag = true;
dot_flag = true;
if (dot_flag) {
ctx.beginPath();
ctx.fillStyle = x;
ctx.fillRect(currX, currY, 2, 2);
ctx.closePath();
dot_flag = false;
}
}
if (res == 'up' || res == "out") {
flag = false;
}
if (res == 'move') {
if (flag) {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
draw();
}
}
}
</script>
<body onload="init()">
<canvas id="can" width="400" height="400" style="position:absolute;top:10%;left:10%;border:2px solid;"></canvas>
<div style="position:absolute;top:12%;left:43%;">Choose Color</div>
<div style="position:absolute;top:15%;left:45%;width:10px;height:10px;background:green;" id="green" onclick="color(this)"></div>
<div style="position:absolute;top:15%;left:46%;width:10px;height:10px;background:blue;" id="blue" onclick="color(this)"></div>
<div style="position:absolute;top:15%;left:47%;width:10px;height:10px;background:red;" id="red" onclick="color(this)"></div>
<div style="position:absolute;top:17%;left:45%;width:10px;height:10px;background:yellow;" id="yellow" onclick="color(this)"></div>
<div style="position:absolute;top:17%;left:46%;width:10px;height:10px;background:orange;" id="orange" onclick="color(this)"></div>
<div style="position:absolute;top:17%;left:47%;width:10px;height:10px;background:black;" id="black" onclick="color(this)"></div>
<div style="position:absolute;top:20%;left:43%;">Eraser</div>
<div style="position:absolute;top:22%;left:45%;width:15px;height:15px;background:white;border:2px solid;" id="white" onclick="color(this)"></div>
<img id="canvasimg" style="position:absolute;top:10%;left:52%;" style="display:none;">
<input type="button" value="save" id="btn" size="30" onclick="save()" style="position:absolute;top:55%;left:10%;">
<input type="button" value="clear" id="clr" size="23" onclick="erase()" style="position:absolute;top:55%;left:15%;">
</body>
</html>
Upvotes: 2
Views: 3847
Reputation: 1165
<canvas id='asdf' width='200px' height='200px'></canvas>
var canvas = document.getElementById("asdf");
var abc = canvas.getContext("2d");
abc.setLineDash([5, 3]);
Upvotes: 0
Reputation: 136627
Your problem is that you are currently drawing a new Path at every mousemove. Dashes are made from the path starting point, to its end.
When you do move your mouse slowly, your are actually generating a lot of really small pathes, smaller than the 5px of your dash-array.
While when you move your mouse faster, the distance between two points is greater than 10px, and so, you can see the dash.
The solution for this problem is to store your points in an array, and redraw a single path from these stored points every time you do a redraw. This way, your pathes will simply get longer, and your dash will be working fine.
In the following example, I even save a new path at each mouseup, so they can have their own dashed
property :
const ctx = c.getContext('2d');
const pathes = []; // this is where we will store all our pathes
let mouse_down = false; // shall we draw ?
c.onmousedown = e => {
// add a new path object
pathes.push({
pts: [], // an array of points
dashed: check.checked // boolean
});
mouse_down = true; // we should draw
}
c.onmouseup = c.onmouseleave = e => mouse_down = false;
c.onmousemove = throttle(e => {
if (!mouse_down) {
return;
} else {
const rec = c.getBoundingClientRect();
// add a new point
addPoint(e.clientX - rec.left, e.clientY - rec.top);
redraw(); // redraw everything
}
});
function redraw() {
ctx.clearRect(0, 0, c.width, c.height); // we clear everything
// and draw every pathes
pathes.forEach(path => {
ctx.setLineDash(path.dashed ? [5, 5] : [0]);
ctx.beginPath();
path.pts.forEach(pt => ctx.lineTo(pt.x, pt.y));
ctx.stroke();
})
}
function addPoint(x, y) {
// append to the last one
const points = pathes[pathes.length - 1].pts;
points.push({
x: x,
y: y
});
}
// just to avoid unnecessary drawings
function throttle(callback) {
if (typeof callback !== 'function')
throw 'A callback function must be passed';
var active = false;
var evt;
var handler = function() {
active = false;
callback(evt);
};
return function handleEvent(e) {
evt = e;
if (!active) {
active = true;
requestAnimationFrame(handler);
}
};
}
canvas {
border: 1px solid
}
<label>dashed : <input type="checkbox" id="check" checked></label><br>
<canvas id="c" width="500" height="500"></canvas>
Upvotes: 5