Change canvas background color to transparent - javascript

I have a text under canvas, i want to show it when i am erasing background color of canvas. Now it is red, when i wrote transparent it does not work. I need to show that text when i draw with the mouse. I tried with rgba too, but is was not working.
please help me if you can
enter code here
var cont = document.getElementById("spots"), // UI elements
canvas = document.getElementById("canvas"),
alpha = document.getElementById("alpha"),
modes = document.getElementById("modes"),
ctx = canvas.getContext("2d"),
isDown = false, // defaults
color = "red";
// set up color palette using a custom "Spot" object
// This will use a callback function when it is clicked, to
// change current color
function Spot(color, cont, callback) {
var div = document.createElement("div");
div.style.cssText = "width:50px;height:50px;border:1px solid #000;margin:0 1px 1px 0;background:" + color;
div.onclick = function() {callback(color)};
cont.appendChild(div);
}
// add some spot colors to our palette container
new Spot(color, cont, setColor);
// this will set current fill style based on spot clicked
function setColor(c) {ctx.fillStyle = c}
// setup defaults
ctx.fillStyle = color;
ctx.globalAlpha = 1;
// events
canvas.onmousedown = function() {isDown = true};
window.onmouseup = function() {isDown = false};
window.onmousemove = function(e) {
if (!isDown) return;
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
ctx.beginPath();
ctx.arc(x, y, 25, 0, 2*Math.PI);
ctx.fill();
};
.main-content{
position: relative;
width: 300px;
height: 300px;
}
.main-text{
position: absolute;
right: 0;
left: 0;
text-align: center;
z-index: 8;
font-size: 35px;
}
#canvas{
background-color: green;
position: absolute;
z-index: 9;
}
<div class="main-content">
<p class="main-text">You Won!!!</p>
<canvas id="canvas" width=300 height=300 style="border: 1px solid green;"></canvas>
<div id="spots"></div>
</div>

I think you would like to get something like the solution in the snippet below.
var cont = document.getElementById("spots"), // UI elements
canvas = document.getElementById("canvas"),
alpha = document.getElementById("alpha"),
modes = document.getElementById("modes"),
ctx = canvas.getContext("2d"),
isDown = false, // defaults
color = "green";
// set up color palette using a custom "Spot" object
// This will use a callback function when it is clicked, to
// change current color
function Spot(color, cont, callback) {
var div = document.createElement("div");
div.style.cssText = "width:50px;height:50px;border:1px solid #000;margin:0 1px 1px 0;background:" + color;
div.onclick = function() {
callback(color)
};
cont.appendChild(div);
}
// add some spot colors to our palette container
new Spot(color, cont, setColor);
// this will set current fill style based on spot clicked
function setColor(c) {
ctx.fillStyle = c
}
// setup defaults
ctx.fillStyle = color;
ctx.globalAlpha = 1;
// create a rectangle using canvas functions, not CSS
// background color.
const createRect = (ctx, width, height) => {
ctx.fillRect(0, 0, width, height)
}
createRect(ctx, 300, 300)
// events
canvas.onmousedown = function() {
isDown = true
};
window.onmouseup = function() {
isDown = false
};
window.onmousemove = function(e) {
if (!isDown) return;
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
// you needed a bit more code here:
ctx.fillStyle = "rgba(255, 255, 255, 0.5)"
ctx.save();
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
ctx.arc(x, y, 25, 0, 2 * Math.PI, false);
ctx.fill();
ctx.restore();
};
.main-content {
position: relative;
width: 300px;
height: 300px;
}
.main-text {
position: absolute;
right: 0;
left: 0;
text-align: center;
z-index: 8;
font-size: 35px;
}
#canvas {
/*background-color: green;*/
position: absolute;
z-index: 9;
}
<div class="main-content">
<p class="main-text">You Won!!!</p>
<canvas id="canvas" width=300 height=300 style="border: 1px solid green;"></canvas>
<div id="spots"></div>
</div>
About canvas global compositions: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

You are halfway there, but there are a couple of things to change.
What you're trying to do is to change the appearance of a css property through the canvas, which does not work like that. You also can not alter the transparency of the canvas, however there are solutions to your case, and it is very simple.
You need to apply a background color to your canvas, then remove the pixels by using the exact same drawing function you have, then you set an extra property called globalCompositeOperation, which is basically the "BlendMode" of photoshop.
So here's your updated code:
var cont = document.getElementById("spots"), // UI elements
canvas = document.getElementById("canvas"),
alpha = document.getElementById("alpha"),
modes = document.getElementById("modes"),
ctx = canvas.getContext("2d"),
isDown = false, // defaults
color = "red";
// set up color palette using a custom "Spot" object
// This will use a callback function when it is clicked, to
// change current color
function Spot(color, cont, callback) {
var div = document.createElement("div");
div.style.cssText = "width:50px;height:50px;border:1px solid #000;margin:0 1px 1px 0;background:" + color;
div.onclick = function() {callback(color)};
cont.appendChild(div);
}
// add some spot colors to our palette container
//new Spot(color, cont, setColor);
// this will set current fill style based on spot clicked
function setColor(c) {ctx.fillStyle = c}
// setup defaults
ctx.fillStyle = color;
ctx.globalAlpha = 1;
// draw the background
ctx.fillRect(0, 0, 300, 300);
// add the 'blend mode effect'
ctx.globalCompositeOperation = 'destination-out';
// events
canvas.onmousedown = function() {isDown = true};
window.onmouseup = function() {isDown = false};
window.onmousemove = function(e) {
if (!isDown) return;
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
ctx.beginPath();
ctx.arc(x, y, 25, 0, 2*Math.PI);
ctx.fill();
};
.main-content{
position: relative;
width: 300px;
height: 300px;
background: blue;
}
.main-text{
position: absolute;
right: 0;
left: 0;
text-align: center;
z-index: 8;
font-size: 35px
}
#canvas{
position: absolute;
z-index: 9;
}
<div class="main-content">
<p class="main-text">You Won!!!</p>
<canvas id="canvas" width=300 height=300 style="border: 1px solid green;"></canvas>
<div id="spots"></div>
</div>
Apart from that tiny change on the JS, I also changed the order of z-index on the CSS. Good luck

Related

I cant put canvas into divs without changing the orientation

The problem is that i want the arrows go row and column but only its only on column
html
<div class="grafica" id="grafica">
<div id="x"></div>
<div id="y"></div>
</div>
css
.grafica{
width: 90%;
height: 95vh;
box-shadow: 7px 10px 30px -1px black;
}
Each arrow its inside of a div, if i dont put the arrows inside the div it work well but i need to do it
javascript
function createArrow(){
const div = document.createElement('div');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 100;
canvas.height = 100;
canvas.className = 'arrow';
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.lineWidth = 3;
ctx.moveTo(50, 50);
ctx.lineTo(0, 0);
ctx.moveTo(20, 10);
ctx.lineTo(0, 0);
ctx.lineTo(10, 20);
ctx.stroke();
document.addEventListener('mousemove', ()=> {
let distanceX = getDistanceBetweenElements(canvas, x);
let distanceY = getDistanceBetweenElements(canvas, y);
let f1 = (k*q1*q2)/distanceX;
let f2 = (k*q1*q2)/distanceY;
console.log(f1, f2);
//canvas.style.transform = `rotate(${a}deg)`;
});
grafica.append(div);
div.append(canvas);
}
Please add the following code to your css:
.grafica > div {
float: left;
}

Canvas: create semi-transparent overlay over entire canvas except one window

I have a rectangular canvas with an image painted on it. As the user moves the cursor over the canvas, I'd like to make the canvas semitransparent except for a small rectangle around the cursor, which I'd like to retain the underlying image content in full opacity.
So my question in brief is how to "mute" the canvas except for a small rectangle.
My first thought was to create a semitransparent overlay over the entire canvas, then just paint the rectangular region that should be full opacity again, but this makes the background disappear while I want to retain it at 0.2 opacity:
var elem = document.querySelector('canvas');
var ctx = elem.getContext('2d');
elem.width = 400;
elem.height = 300;
ctx.fillStyle = '#ff0000';
ctx.fillRect(0, 0, elem.width, elem.height);
elem.addEventListener('mousemove', function(e) {
ctx.globalAlpha = 0.2;
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, elem.width, elem.height);
var x = e.clientX;
var y = e.clientY;
var d = 30;
ctx.fillStyle = '#ff0000';
ctx.fillRect(x-d, y-d, d*2, d*2);
})
<canvas/>
Does anyone know the most performant way to mute the background to 0.2 opacity while retaining the rectangle around the cursor at full opacity? Any pointers would be super helpful!
Here's the two canvas method:
var elemA = document.querySelector('#a');
var elemB = document.querySelector('#b');
var ctx = elemA.getContext('2d');
var bctx = elemB.getContext('2d');
elemA.width = elemB.width = 400;
elemA.height = elemB.height = 300;
ctx.fillStyle = '#ff0000';
ctx.fillRect(0, 0, elemA.width, elemA.height);
elemB.addEventListener('mousemove', function(e) {
bctx.clearRect(0, 0, elemB.width, elemB.height);
var x = e.clientX;
var y = e.clientY;
var x0 = x-10;
var x1 = x+10;
var y0 = y-10;
var y1 = y+10;
// draw boxes; origin is top left
bctx.globalAlpha = 0.8;
bctx.fillStyle = '#ffffff';
bctx.fillRect(0, 0, elemA.width, y0); // top
bctx.fillRect(0, y0, x0, elemB.height+20); // left
bctx.fillRect(x0, y1, elemB.width+20, elemB.height); // bottom
bctx.fillRect(x1, y0, elemA.width, y1-y0); // right
})
* {
margin: 0;
}
#c {
position: relative;
}
#a, #b {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
#b {
z-index: 1;
opacity: 0.5;
}
<div id='c'>
<canvas id='a'></canvas>
<canvas id='b'></canvas>
</div>

HTML5 Canvas positioning mouse clicking broken

I have one canvas element that when positioned with css, fails to register mouse clicks and move elements drawn within canvas. If you do not position the canvas the mouse clicks register and selected elements will get moved but I need to be able to position this canvas layer with left and top so I can put another canvas underneath yet retain my mouse click and move ability.
This is what I am using to get my x/y click coordinates
var br = canvas.getBoundingClientRect(); //bounding rectangle
var mousex = parseInt(e.clientX-br.left);
var mousey = parseInt(e.clientY-br.top);
I have changed clientX and clientY to use pageX and pageY as others have suggested but the issue still remains, even if I place the canvas in another div and position accordingly. I've also subtracted the amount I'm moving the container over from mousex but without success. Even positioning with flex breaks the functionality.
Is this simply a limitation of canvas?
** Clarification, if it wasn't clear before, as detecting the mouse click is NOT the issue, the issue is when I position the canvas inside the DOM, the clicks are no longer registered. **
You can do it using offsetLeft and offsetTop. Here is a demo:
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
targetPos = {x: 150, y: 50};
// Initial render
render();
// Whenever the user clicks on the canvas, update the position
canvas.addEventListener('click', updateTargetPos);
function updateTargetPos(e) {
targetPos = {
x: e.pageX - canvas.offsetLeft,
y: e.pageY - canvas.offsetTop
};
render();
}
function render() {
renderBackground();
renderTarget();
}
function renderBackground() {
ctx.fillStyle = "#333";
ctx.fillRect(0, 0, 300, 200);
}
function renderTarget() {
var size = 10;
ctx.fillStyle = "#f00";
ctx.fillRect(targetPos.x - size/2, targetPos.y - size/2, size, size);
}
body {
font-family: Arial, Helvetica, sans-serif;
}
#canvas {
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -150px;
}
<p>Click on this absolutely positioned canvas:</p>
<canvas id="canvas" width="300" height="100"></canvas>
I was editing this in devtools on the fly which was causing issues with canvas and being able to get the correct x/y of the mouse click. When I would adjust positioning in dev tools then attempt to move the square it would not work. Modifying my stylesheet prior to loading into the dom in this instance yielded the correct result as the calculations would need to be run again after manipulating the canvas positioning.
For those that needed a visual, here is some code. To replicate the problem you can try to add justify-content: center to the .flex class then attempt to move the green square to see that you can't move the square, or rather the spot that is defined to be clickable is not located over the box.
var canvas = document.getElementById("canvasArea");
var ctx = canvas.getContext("2d");
var canvas2 = document.getElementById("canvas2");
var ctx2 = canvas2.getContext("2d");
var x = canvas.width/2;
var y = canvas.height-10;
var dx = 2;
var dy = -2;
var boundingRectangle = canvas.getBoundingClientRect();
var moveBox = false;
var selectedBox;
function box(x, y) {
this.color = "green";
this.xPos = x;
this.yPos = y;
this.width = 50;
this.height = 50;
}
box.prototype.drawBox = function() {
ctx.fillStyle = this.color;
ctx.fillRect(this.xPos, this.yPos, this.width, this.height);
};
var abox = new box(x, 30);
canvas.addEventListener("mousedown", function(e) {
e.preventDefault();
e.stopPropagation();
var mousex = parseInt(e.clientX-boundingRectangle.left);
var mousey = parseInt(e.clientY-boundingRectangle.top);
if(mousex > abox.xPos && mousex < abox.xPos + abox.width && mousey > abox.yPos && mousey < abox.yPos + abox.height) {
moveBox = true;
selectedBox = "abox"
}
}, true);
canvas.addEventListener("mousemove", function(e) {
e.preventDefault();
e.stopPropagation();
if(moveBox) {
if(selectedBox == "abox") {
abox.xPos = e.offsetX;
abox.yPos = e.offsetY;
}
}
})
canvas.addEventListener("mouseup", function(e) {
e.preventDefault();
e.stopPropagation();
moveBox = false;
})
function bg(ctx2) {
var alpha = ctx2.globalAlpha;
ctx2.save();
ctx2.beginPath();
ctx2.moveTo(142.5, 23.7);
ctx2.lineTo(85.9, 0.0);
ctx2.lineTo(0.0, 204.8);
ctx2.lineTo(56.6, 228.5);
ctx2.lineTo(142.5, 23.7);
ctx2.closePath();
ctx2.fillStyle = "rgb(31, 155, 215)";
ctx2.fill();
ctx2.beginPath();
ctx2.moveTo(235.1, 23.7);
ctx2.lineTo(178.5, 0.0);
ctx2.lineTo(92.6, 204.8);
ctx2.lineTo(149.2, 228.5);
ctx2.lineTo(235.1, 23.7);
ctx2.closePath();
ctx2.fillStyle = "rgb(77, 75, 159)";
ctx2.fill();
ctx2.beginPath();
ctx2.moveTo(330.5, 23.7);
ctx2.lineTo(273.9, 0.0);
ctx2.lineTo(188.0, 204.8);
ctx2.lineTo(244.6, 228.5);
ctx2.lineTo(330.5, 23.7);
ctx2.closePath();
ctx2.fillStyle = "rgb(176, 67, 152)";
ctx2.fill();
ctx2.beginPath();
ctx2.moveTo(435.4, 23.7);
ctx2.lineTo(378.8, 0.0);
ctx2.lineTo(292.9, 204.8);
ctx2.lineTo(349.5, 228.5);
ctx2.lineTo(435.4, 23.7);
ctx2.closePath();
ctx2.fillStyle = "rgb(69, 174, 77)";
ctx2.fill();
ctx2.beginPath();
ctx2.moveTo(541.4, 23.7);
ctx2.lineTo(484.7, 0.0);
ctx2.lineTo(398.9, 204.8);
ctx2.lineTo(455.5, 228.5);
ctx2.lineTo(541.4, 23.7);
ctx2.closePath();
ctx2.fillStyle = "rgb(237, 127, 34)";
ctx2.fill();
ctx2.restore();
}
function render() {
requestAnimationFrame(render);
ctx.clearRect(0, 0, canvas.width, canvas.height);
abox.drawBox();
}
render();
bg(ctx2)
.flex {
width: 100%;
height: 100%;
display: flex;
}
canvas#canvasArea {
position: absolute;
display: inline-block;
}
.container {
position: relative;
left: 150px;
top: -20px;
z-index: 999999;
}
.container {
position: relative;
}
.canvasbg {
position: absolute;
width: 200px;
height: 150px;
margin: 20px 0;
border: none;
background: #000000;
}
<div class="flex">
<div class="container">
<div class="canvasbg">
<canvas id="canvasArea" width="220" height="150">
Your browser does not support HTML5 canvas.
</canvas>
</div>
</div>
<canvas id="canvas2" width="540" height="177"></canvas>

HTML5 canvas lines are blury in the arc() function

I am having trouble with blurry lines on the element.
ctx.moveTo(2,2);
ctx.lineTo(50,2);
ctx.arc(27,2,25,0,3.1415926);
ctx.stroke();
I tried making the linewidth 0.5 but that didn't fix it. Everything I try doesn't seem to do anything.
The result turns out very pixely.
See result on https://rawgit.com/Mythius/uploads/master/Platformer.html
If anyone knows how to fix this please let me know.
Do not set your canvas size in CSS alone.
The displayed size of the canvas can be changed using a stylesheet. The image is scaled during rendering to fit the styled size. If your renderings seem distorted, try specifying your width and height attributes explicitly in the attributes, and not using CSS.
Default canvas size
var canvas = document.getElementById('cnvs');
var ctx = canvas.getContext('2d');
ctx.lineWidth = 1;
ctx.translate(0.5, 0.5);
draw();
function draw() {
ctx.moveTo(0, 0);
ctx.lineTo(50, 0);
ctx.arc(25, 0, 25, 0, Math.PI);
ctx.stroke();
}
body {
background-color: #aaa;
}
#cnvs {
background-color: #fff;
border: 1px solid #000;
}
<canvas id="cnvs"></canvas>
With size specified on the canvas element attributes
var canvas = document.getElementById('cnvs');
var ctx = canvas.getContext('2d');
ctx.lineWidth = 1;
ctx.translate(0.5, 0.5);
draw();
function draw() {
ctx.moveTo(0, 0);
ctx.lineTo(50, 0);
ctx.arc(25, 0, 25, 0, Math.PI);
ctx.stroke();
}
body {
background-color: #aaa;
}
#cnvs {
width: 500px;
height: 500px;
background-color: #fff;
border: 1px solid #000;
}
<canvas id="cnvs" width="500" height="500"></canvas>
As an addendum to Sébastien's answer: The 'blockiness' of a canvas image sits at the confluence of screen resolution, canvas dimensions, and styling properties. Depending on these factors an image can appear more or less blocky/sharp - but there's only so much you can do.
Some people say that drawing to a larger canvas and styling it proportionally smaller improves the appearance of fine detail -- by the principle that small images made large look block so large images made small will look less blocky -- while others are not convinced.
Below is a snippet which draws the same content onto four canvases at two different sizes and resolutions, but all canvas are styled to the same on-screen dimensions. How blocky are they on your gear? To me they look pretty much the same, but when I save them I do notice a difference.
(function(doc) {
function $(a) {
switch (a.slice(0, 1)) {
case "#":
return doc.getElementById(a.slice(1));
case ".":
return [].slice.call(doc.getElementsByClassName(a.slice(1)));
}
}
function save(e) {
var cnv = $(e.target.getAttribute('data-canvas')),
lnx = $('#savelink');
lnx.href = cnv.toDataURL();
lnx.download = e.target.getAttribute('data-res') +
'_ ' + cnv.width + 'x' +
cnv.height + '.png';
lnx.click();
}
function lowRes(cnv, ctx) {
var img = new Image;
img.addEventListener('load', function() {
ctx.clearRect(0, 0, cnv.width, cnv.height);
ctx.drawImage(this, 0, 0);
});
img.src = cnv.toDataURL('image/jpeg', 0.64);
};
function draw(id, wh, lw, res) {
var cnv = $(id),
ctx = cnv.getContext('2d'),
xy = wh / 2,
fc = 8,
shrink = (xy * 0.9) / fc,
flag = !1;
cnv.width = wh,
cnv.height = wh,
ctx.lineWidth = lw;
ctx.fillStyle = '#eee';
ctx.fillRect(0,0,cnv.width,cnv.height);
ctx.beginPath();
ctx.moveTo(0, xy);
ctx.lineTo(cnv.width, xy);
while (--fc) {
ctx.arc(xy, xy, shrink * fc, 0, Math.PI, flag);
flag = !flag;
}
ctx.stroke();
ctx.fillStyle = '#777';
ctx.font = Math.round(cnv.height * 0.025) + 'px serif';
ctx.textAlign = 'right';
ctx.textBaseline = 'middle';
ctx.beginPath();
ctx.fillText(
('lineWidth = ' + lw +
', width/height = ' + wh + 'px, ' +
(res ? 'low-res' : 'hi-res')),
Math.round(cnv.width * 0.9),
Math.round(cnv.height * 0.96)
);
res && lowRes(cnv, ctx);
}
doc.addEventListener('DOMContentLoaded', function() {
[
['#c1', 500, 1, !1],
['#c2', 1500, 3, !1],
['#c3', 500, 1, !0],
['#c4', 1500, 3, !0]
].forEach(function(n) {
draw.apply(null, n);
});
$('.save').forEach(function(n) {
n.addEventListener('click', save, !1);
});
}, false);
}(document));
.ch {
position:relative;
width:500px;
height:500px;
border:1px solid #999;
margin:2rem auto;
}
.ch canvas {
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
display:block;
}
.ch .save {
position:absolute;
top:2%;
left:2%;
color:#aaa;
font-size:2rem;
font-weight:600;
cursor:pointer;
display:inline-block;
transition:color 333ms;
}
.ch .save:hover {
color:#000;
}
<div class="ch">
<canvas id="c1"></canvas>
<div class="save" title=" Save Image " data-canvas="#c1" data-res="hi">⇩</div>
</div>
<div class="ch">
<canvas id="c2"></canvas>
<div class="save" title=" Save Image " data-canvas="#c2" data-res="hi">⇩</div>
</div>
<div class="ch">
<canvas id="c3"></canvas>
<div class="save" title=" Save Image " data-canvas="#c3" data-res="lo">⇩</div>
</div>
<div class="ch">
<canvas id="c4"></canvas>
<div class="save" title=" Save Image " data-canvas="#c4" data-res="lo">⇩</div>
</div>
<a id="savelink" href="" download="" target="_blank"></a>

Draw dashed and dotted rectangles on canvas in the same way css border works: draw 4 same edges

My use-case is to mimic css border rendering. Is it possible, using the CanvasRenderingContext2D::rect method with CanvasRenderingContext2D::setLineDash to simulate same border drawing as css renderer does, like border: 5px dashed red. Consider this example:
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
ctx.lineWidth = 5
ctx.strokeStyle = 'red'
ctx.lineCap = 'square'
ctx.setLineDash([10, 10]);
ctx.beginPath();
ctx.moveTo(2.5,2.5);
ctx.rect(2.5, 2.5, 195, 65);
ctx.stroke();
div {
border: 5px dashed red;
width: 200px;
height: 70px;
box-sizing: border-box;
margin-bottom: 5px;
}
canvas {
display: block;
width: 200px;
height: 70px;
}
<div></div>
<canvas width=200 height=70></canvas>
You may notice the problem is on edges.
I was trying to modify the gaps and dash sizes, but it seems impossible to get the same behaviour as in the css example: the lines on edges are bigger then the lines on the sides. As a workaround I can imagine to draw every side with a line, but I would like to use the rect method to draw in one stroke.
Thank you in advance.
CSS border-style: dashed algorithm is not tied by specs, so it will be impossible to render exactly the same in the canvas API.
Then, you've got to know that even CSS renders it line by line: border is a shorthand for all the border-top-XXX, border-right-XXX, border-bottom-XXX, border-left-XXX.
And that's why it behaves like that : each border has its line-dash set independently of the others.
Anyway, if you want to do it with the canvas API, the easiest solution is to do the same, using four lines, and setting their line-dash separately.
Here is a rough attempt at normalizing the dashes in order to get them always start and end at edges:
var ctx = c.getContext('2d');
ctx.lineCap = 'square';
// returns a normalized dashArray per segment
// This in no way does the same as any browser's implementation,
// this is just a lazy way to always get dashes start and end at edges
function getLineDash(x1, y1, x2, y2) {
var length = Math.hypot((x2 - x1), (y2 - y1));
var dash_length = length / 8;
var nb_of_dashes = length / dash_length;
var dash_gap = (dash_length * 0.66);
dash_length -= dash_gap * 0.33;
return [dash_length, dash_gap];
}
function draw() {
ctx.lineWidth = lineWidth_.value;
ctx.clearRect(0, 0, c.width, c.height);
var points = [
[x1_.value, y1_.value],
[x2_.value, y2_.value],
[x3_.value, y3_.value],
[x4_.value, y4_.value]
];
points.forEach(function(pt, i) {
var next = points[(i + 1) % points.length];
ctx.beginPath();
ctx.moveTo(pt[0], pt[1]);
ctx.lineTo(next[0], next[1]);
ctx.setLineDash(getLineDash(pt[0], pt[1], next[0], next[1]));
ctx.stroke();
});
}
draw();
document.oninput = function(e) {
if (e.target.parentNode.parentNode === inputs_) {
draw();
}
}
label {
display: inline-block;
}
input {
max-width: 50px;
}
<div id="inputs_">
<label>x1<input type="number" id="x1_" value="10"></label>
<label>y1<input type="number" id="y1_" value="25"></label>
<label>x2<input type="number" id="x2_" value="350"></label>
<label>y2<input type="number" id="y2_" value="25"></label>
<label>x3<input type="number" id="x3_" value="350"></label>
<label>y3<input type="number" id="y3_" value="225"></label>
<label>x4<input type="number" id="x4_" value="10"></label>
<label>y4<input type="number" id="y4_" value="225"></label>
<label>lineWidth<input type="number" id="lineWidth_" value="3"></label>
</div>
<canvas id="c" width="400" height="400"></canvas>
So now, if you only want to use XXXRect, you can also create a single huge dash-array containing all of the dashes...
var ctx = c.getContext('2d');
ctx.lineCap = 'square';
function getRectDashes(width, height) {
var w_array = getLineDashes(width, 0, 0, 0);
var h_array = getLineDashes(0, height, 0, 0);
dashArray = [].concat.apply([], [w_array, 0, h_array, 0, w_array, 0, h_array]);
return dashArray;
}
// same as previous snippet except that it does return all the segment's dashes
function getLineDashes(x1, y1, x2, y2) {
var length = Math.hypot((x2 - x1), (y2 - y1));
var dash_length = length / 8;
var nb_of_dashes = length / dash_length;
var dash_gap = dash_length * 0.66666;
dash_length -= dash_gap * 0.3333;
var total_length = 0;
var dasharray = [];
var next;
while (total_length < length) {
next = dasharray.length % 2 ? dash_gap : dash_length;
total_length += next;
dasharray.push(next);
}
return dasharray;
}
function draw() {
ctx.clearRect(0, 0, c.width, c.height);
ctx.lineWidth = lineWidth_.value;
var w = width_.value,
h = height_.value;
ctx.setLineDash(getRectDashes(w, h));
ctx.strokeRect(20, 20, w, h);
}
draw();
document.oninput = function(e) {
if (e.target.parentNode.parentNode === inputs_)
draw();
};
label {
display: inline-block;
}
input {
max-width: 50px;
}
<div id="inputs_">
<label>width<input type="number" id="width_" value="200"></label>
<label>height<input type="number" id="height_" value="225"></label>
<label>lineWidth<input type="number" id="lineWidth_" value="3"></label>
</div>
<canvas id="c" width="400" height="400"></canvas>

Categories