i am trying to create the puzzle pieces with the effect in the image but i am unable to get it through.
Can anyone help me how to create the pieces with canvas and html5.
thanks
You can draw a beveled edge puzzle piece this way.
Draw the part of the image you want to use for the piece positioned correctly
Build a path for the puzzle piece
Punch out the piece using composite mode destination-in
Add rect/bounding box to path
Define dark shadow and fill using composite mode source-atop
Move shadow and change color to bright, fill again
What you need to do in addition is to calculate the offset for the image you're using for each piece. You could also draw each piece into a separate canvas and extract the canvas as image which you later use to draw/show the pieces.
Example
var ctx = c.getContext("2d"), img = new Image;
img.onload = demo;
img.src = "https://pixeloceanblog.files.wordpress.com/2016/05/pres_frontcorner.jpg";
function demo() {
ctx.translate(-20,-90); // just to compensate for demo puzzle piece position
// 1) Draw puzzle pieze graphics
ctx.drawImage(this, -220, -110);
// 2) build path for puzzle piece
puzzlePath(ctx);
// 3) Punch out piece
ctx.globalCompositeOperation = "destination-in";
ctx.fill();
// 4) Add rect to make stencil
ctx.rect(0, 0, c.width, c.height);
// 5) Build dark shadow
ctx.shadowBlur = 7;
ctx.shadowOffsetX = -7;
ctx.shadowOffsetY = -7;
ctx.shadowColor = "rgba(0,0,0,0.8)";
// 6) Draw stencil with shadow but only on non-transparent pixels
ctx.globalCompositeOperation = "source-atop";
ctx.fill();
// 7) move shadow and change color to white transparent
ctx.shadowOffsetX = 7;
ctx.shadowOffsetY = 7;
ctx.shadowColor = "rgba(255,255,255,0.8)";
ctx.fill();
// DONE!
// Puzzle path for demo
function puzzlePath(ctx) {
ctx.beginPath();
ctx.moveTo(36.421871,256.82809);
ctx.bezierCurveTo(36.981411,272.02753,42.075181,289.07672,52.984371,298.17184);
ctx.bezierCurveTo(81.172101,311.4011,68.157281,279.26413,87.671871,275.48434);
ctx.bezierCurveTo(107.18646,271.70455,108.26562,294.39059,108.26562,294.39059);
ctx.lineTo(108.26562,294.39059);
ctx.lineTo(108.26562,392.14059);
ctx.bezierCurveTo(108.26562,392.14059,205.20313,392.01559,205.20312,392.01559);
ctx.bezierCurveTo(205.20312,392.01559,227.88915,390.93642,224.10937,371.42184);
ctx.bezierCurveTo(221.03829,355.56624,199.23133,361.18908,198.26562,348.73434);
ctx.bezierCurveTo(198.04276,345.86017,198.94138,342.01954,201.42187,336.73434);
ctx.bezierCurveTo(210.51699,325.82514,227.56618,320.73138,242.76562,320.17184);
ctx.bezierCurveTo(248.37657,319.96528,253.65356,320.45514,258.26562,321.42184);
ctx.bezierCurveTo(258.47031,321.44665,258.68579,321.48913,258.89062,321.51559);
ctx.bezierCurveTo(272.00503,323.2095,285.30842,328.27739,292.98437,337.48434);
ctx.bezierCurveTo(306.21364,365.67208,274.07665,352.65725,270.29687,372.17184);
ctx.bezierCurveTo(267.54294,386.39004,278.77521,390.78229,285.10937,392.14059);
ctx.lineTo(388.01562,392.14059);
ctx.lineTo(388.01562,290.54684);
ctx.bezierCurveTo(389.23388,284.34562,393.50469,272.30575,408.14062,275.14059);
ctx.bezierCurveTo(427.65522,278.92038,414.64038,311.02611,442.82812,297.79684);
ctx.bezierCurveTo(452.03508,290.1209,457.10296,276.8175,458.79687,263.70309);
ctx.bezierCurveTo(458.82334,263.49827,458.86581,263.28278,458.89062,263.07809);
ctx.bezierCurveTo(459.85733,258.46605,460.34718,253.18904,460.14062,247.57809);
ctx.bezierCurveTo(459.58109,232.37865,454.48732,215.32946,443.57812,206.23434);
ctx.bezierCurveTo(415.3904,193.00508,428.40521,225.14205,408.89062,228.92184);
ctx.bezierCurveTo(389.37605,232.70164,388.29687,210.01559,388.29687,210.01559);
ctx.lineTo(388.01562,112.82809);
ctx.bezierCurveTo(388.01562,112.82809,289.76561,113.4531,289.76562,113.45309);
ctx.bezierCurveTo(289.76562,113.45309,267.07957,114.53227,270.85937,134.04684);
ctx.bezierCurveTo(274.63916,153.56143,306.77612,140.54662,293.54687,168.73434);
ctx.bezierCurveTo(284.45175,179.64354,267.40256,184.73731,252.20312,185.29684);
ctx.bezierCurveTo(246.59217,185.5034,241.31517,185.01355,236.70312,184.04684);
ctx.bezierCurveTo(236.49843,184.02203,236.28293,183.97956,236.07812,183.95309);
ctx.bezierCurveTo(222.96371,182.25918,209.6603,177.1913,201.98437,167.98434);
ctx.bezierCurveTo(199.50388,162.69914,198.60526,158.85851,198.82812,155.98434);
ctx.bezierCurveTo(199.79383,143.5296,221.56954,149.15245,224.64062,133.29684);
ctx.bezierCurveTo(228.16732,115.08894,209.3009,113.03555,206.67187,112.82809);
ctx.lineTo(108.26562,112.82809);
ctx.lineTo(108.26562,214.98434);
ctx.bezierCurveTo(106.78368,221.38993,102.27374,231.94857,88.421871,229.26559);
ctx.bezierCurveTo(68.907281,225.48581,81.922111,193.38007,53.734371,206.60934);
ctx.bezierCurveTo(44.527431,214.28529,39.459541,227.58868,37.765621,240.70309);
ctx.bezierCurveTo(37.739161,240.90792,37.696681,241.1234,37.671871,241.32809);
ctx.bezierCurveTo(36.705171,245.94014,36.215311,251.21714,36.421871,256.82809);
}
}
body {background:#777}
<canvas id=c width=500 height=500></canvas>
Using masking to Apply a inner bevel FX
A very quick answer. I am sure someone can improve on this and are welcome to.
Function takes an image and returns a new image with a bevel applied.
Function arguments
image. The image to apply the bevel to
amount. The strength of the bevel 0 is none and 1 is full
offsetX
offsetY. The offset of the bevel.
blur. The blur. Must be greater or equal to 0
type. The type of bevel as string, ethier, 'shadow', 'light' or as a colour eg
'black'
Returns an image (as canvas)
Example usage
var image = new Image();
image.src = "imageURL.png";
image.onload = function(){
var bevImage = innerBevel(this,0.5,-4,-4,4,"shadow"); // bevel shadow
bevImage = innerBevel(bevImage,0.5,4,4,4,"light"); // bevel highlight
if(typeof ctx !== 'undefined'){
ctx.drawImage(bevImage,0,0); // draw the image to the current context
}
}
The function
function innerBevel(image, amount, offsetX, offsetY, blur, type){
var c = document.createElement("canvas");
c.width = image.width + (Math.abs(offsetX) + blur) * 2; // cludge could be a better fit
c.height = image.height + (Math.abs(offsetY) + blur) * 2; // cludge could be a better fit
var ctx1 = c.getContext("2d");
ctx1.fillRect(0,0,c.width,c.height); // fill pixels
// create the shadow mask
ctx1.globalCompositeOperation = "destination-out";
ctx1.drawImage(image, Math.abs(offsetX)+blur, Math.abs(offsetY)+blur); // create inverse mask
// create second image.
var c1 = document.createElement("canvas");
c1.width = image.width;
c1.height = image.height;
var ctx2 = c1.getContext("2d");
// copy the image
ctx2.drawImage(image,0,0);
// create the shadow and draw it as a shadow from the mask
ctx2.save();
if(type === "shadow"){
ctx2.shadowColor = "black";
ctx2.globalCompositeOperation = "multiply";
}else
if(type === "light"){
ctx2.shadowColor = "White";
ctx2.globalCompositeOperation = "lighter";
}else{
ctx2.shadowColor = type;
}
ctx2.globalAlpha = amount;
ctx2.shadowOffsetX = offsetX;
ctx2.shadowOffsetY = offsetY;
ctx2.shadowBlur = blur;
ctx2.drawImage(c,-(Math.abs(offsetX) + blur), -(Math.abs(offsetY) + blur)); // create inverse mask
ctx2.restore(); // remove the shadow settings
// mask out the unwanted pixels
ctx2.globalCompositeOperation = "destination-out";
ctx2.drawImage(c,-(Math.abs(offsetX) + blur), -(Math.abs(offsetY) + blur));
ctx2.globalCompositeOperation = "source-over";
return c1; // return the new beveled image;
}
How to draw outer and inner border around any canvas shape?
I'm drawing several stroke-only shapes on an html canvas, and I would like to draw an inner and outer border around them.
draft example:
Is there a generic why to do it for any shape (assuming it's a closed stroke-only shape)?
Two methods
There is no inbuilt way to do this and there are two programmatic ways that I use. The first is complicated and involves expanding and contracting the path then drawing along that path. This works for most situations but will fail in complex situation, and the solution has many variables and options to account for these complications and how to handle them.
The better of the two
The second and easiest way that I present below is by using the ctx.globalCompositeOperation setting to mask out what you want drawn or not. As the stroke is drawn along the center and the fill fills up to the center you can draw the stroke at twice the desired width and then either mask in or mask out the inner or outer part.
This does become problematic when you start to create very complex images as the masking (Global Composite Operation) will interfere with what has already been drawn.
To simplify the process you can create a second canvas the same size as the original as a scratch space. You can then draw the shape on he scratch canvas do the masking and then draw the scratch canvas onto the working one.
Though this method is not as fast as computing the expanded or shrunk path, it does not suffer from the ambiguities faced by moving points in the path. Nor does this method create the lines with the correct line join or mitering for the inside or outside edges, for that you must use a the other method. For most purposes the masking it is a good solution.
Below is a demo of the masking method to draw an inner or outer path. If you modify the mask by including drawing a stroke along with the fill you can also set an offset so that the outline or inline will be offset by a number of pixels. I have left that for you. (hint add stroke and set the line width to twice the offset distance when drawing the mask).
var demo = function(){
/** fullScreenCanvas.js begin **/
var canvas = ( function () {
canvas = document.getElementById("canv");
if(canvas !== null){
document.body.removeChild(canvas);
}
// creates a blank image with 2d context
canvas = document.createElement("canvas");
canvas.id = "canv";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
canvas.style.zIndex = 1000;
canvas.ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
return canvas;
})();
var ctx = canvas.ctx;
/** fullScreenCanvas.js end **/
/** CreateImage.js begin **/
// creates a blank image with 2d context
var createImage = function(w,h){
var image = document.createElement("canvas");
image.width = w;
image.height =h;
image.ctx = image.getContext("2d");
return image;
}
/** CreateImage.js end **/
// define a shape for demo
var shape = [0.1,0.1,0.9,0.1,0.5,0.5,0.8,0.9,0.1,0.9];
// draws the shape as a stroke
var strokeShape = function (ctx) {
var w, h, i;
w = canvas.width;
h = canvas.height;
ctx.beginPath();
ctx.moveTo(shape[0] *w, shape[1] *h)
for (i = 2; i < shape.length; i += 2) {
ctx.lineTo(shape[i] * w, shape[i + 1] * h);
}
ctx.closePath();
ctx.stroke();
}
// draws the shape as filled
var fillShape = function (ctx) {
var w, h, i;
w = canvas.width;
h = canvas.height;
ctx.beginPath();
ctx.moveTo(shape[0] * w,shape[1] * h)
for (i = 2; i < shape.length; i += 2) {
ctx.lineTo(shape[i]*w,shape[i+1]*h);
}
ctx.closePath();
ctx.fill();
}
var drawInOutStroke = function(width,style,where){
// clear the workspace
workCtx.ctx.globalCompositeOperation ="source-over";
workCtx.ctx.clearRect(0, 0, workCtx.width, workCtx.height);
// set the width to double
workCtx.ctx.lineWidth = width*2;
workCtx.ctx.strokeStyle = style;
// fill colour does not matter here as its not seen
workCtx.ctx.fillStyle = "white";
// can use any join type
workCtx.ctx.lineJoin = "round";
// draw the shape outline at double width
strokeShape(workCtx.ctx);
// set comp to in.
// in means leave only pixel that are both in the source and destination
if (where.toLowerCase() === "in") {
workCtx.ctx.globalCompositeOperation ="destination-in";
} else {
// out means only pixels on the destination that are not part of the source
workCtx.ctx.globalCompositeOperation ="destination-out";
}
fillShape(workCtx.ctx);
ctx.drawImage(workCtx, 0, 0);
}
// clear in case of resize
ctx.globalCompositeOperation ="source-over";
ctx.clearRect(0,0,canvas.width,canvas.height);
// create the workspace canvas
var workCtx = createImage(canvas.width, canvas.height);
// draw the outer stroke
drawInOutStroke((canvas.width + canvas.height) / 45, "black", "out");
// draw the inner stroke
drawInOutStroke((canvas.width + canvas.height) / 45, "red", "in");
// draw the shape outline just to highlight the effect
ctx.strokeStyle = "white";
ctx.lineJoin = "round";
ctx.lineWidth = (canvas.width + canvas.height) / 140;
strokeShape(ctx);
};
// run the demo
demo();
// incase fullscreen redraw it all
window.addEventListener("resize",demo)
I am new to canvas, I have an image myimg.jpg, I have converted this image into canvas and i am trying to apply some pattern image for heel.
I am not able to do it. Here is my screenshot:
How can I get it done.
<div id="myId">
<canvas id="canvaswrapper" width="660" height="540"></canvas>
</div>
function drawImage(){
var ctx = $("canvas")[0].getContext("2d"),
img = new Image();
img.onload = function(){
ctx.drawImage(img, 0, 0, 500, 500);
ctx.beginPath();
var img2= new Image();
var w;
var h;
img2.src = "http://www.gravatar.com/avatar/e555bd971bc2f4910893cd5b785c30ff?s=128&d=identicon&r=PG";
var pattern = ctx.createPattern(img2, "repeat");
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, w, h);
ctx.arc(300,305,50,0,2*Math.PI);
ctx.fill();
ctx.stroke();
};
img.src = "myimg.jpg";
}
drawImage();
You can define the area you want to fill using an image mask that fits on top of your image - this step is something for Photoshop/GIMP.
For example, having your shoe as-is:
Create a mask for it leaving the heal in the original position (it makes it easier to draw it back in - you can always crop it and draw it using an offset instead). Important: background must be transparent:
Then super-impose the pattern using these steps:
Load the pattern and define is as a fill-pattern
Draw the mask into the empty canvas
Optional step: Adjust transformations if needed (translate, scale)
Choose composite mode "source-atop"
Fill the canvas
Choose composite mode "destination-atop"
Draw the main image on top (which will show behind the mask/pattern)
Optional step: draw in original mask image using blending mode "multiply" to add shadow and highlights (does not work in IE). This will help creating an illusion of depth. For IE, drawing it on top using a reduced alpha or a separate image only containing shadows etc. can be an option
Result
Example
var iShoe = new Image, iMask = new Image, iPatt = new Image, count = 3;
iShoe.onload = iMask.onload = iPatt.onload = loader;
iShoe.src = "http://i.stack.imgur.com/hqL1C.png";
iMask.src = "http://i.stack.imgur.com/k5XWN.png";
iPatt.src = "http://i.stack.imgur.com/CEQ10.png";
function loader() {
if (--count) return; // wait until all images has loaded
var ctx = document.querySelector("canvas").getContext("2d"),
pattern = ctx.createPattern(iPatt, "repeat");
// draw in mask
ctx.drawImage(iMask, 0, 0);
// change comp mode
ctx.globalCompositeOperation = "source-atop";
// fill mask
ctx.scale(0.5, 0.5); // scale: 0.5
ctx.fillStyle = pattern; // remember to double the area to fill:
ctx.fillRect(0, 0, ctx.canvas.width*2, ctx.canvas.height*2);
ctx.setTransform(1,0,0,1,0,0); // reset transform
// draw shoe behind mask
ctx.globalCompositeOperation = "destination-atop";
ctx.drawImage(iShoe, 0, 0);
// to make it more realistic, add mask in blend mode (does not work in IE):
ctx.globalCompositeOperation = "multiply";
if (ctx.globalCompositeOperation === "multiply") {
ctx.drawImage(iMask, 0, 0);
}
}
<canvas width=281 height=340></canvas>
I have two canvas elements and need them to be resized on buttons click.
<div class="sDetails"><div>
<div id="canvasDiv" style="width: 310px;"><canvas id="canvasGraph"></canvas></div></div>
<div class="kDetails"><div><div>
<div id="canvasDiv" style="width: 310px; height: 240px;"><canvas id="canvasGraph"></canvas></div></div>
and the script:
var sketch;var sketch_sl;var onPaint;var canvas=null;var ctx=null;var tmp_ctx=null;
function drawCanvas(div) {
canvas = document.querySelector(div + " #canvasGraph");
ctx = canvas.getContext('2d');
sketch = document.querySelector(div + " #canvasDiv");
sketch_sl = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width'));
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
tmp_canvas = document.createElement('canvas');
tmp_ctx = tmp_canvas.getContext('2d');
tmp_canvas.id = 'tmp_canvas';
tmp_canvas.width = canvas.width;
tmp_canvas.height = canvas.height;
sketch.appendChild(tmp_canvas);
the redraw function:
// here I must redraw my lines resized 2 times ( *cScale ) where cScale=2 or =1
function drawScales(ctx, canvas)
ctx.strokeStyle = 'green';
ctx.fillStyle = 'green';
ctx.beginPath();
ctx.moveTo(5, 0);
ctx.lineTo(0, canvas.height);
scaleStep = 24*cScale;
for some reason it works really bad, old positions stay.
Is there a way to completely delete the whole canvas and append it or redraw it completely?
I tried canvas.width=canvas.width, tried ctx.clearRect(0, 0, canvas.width, canvas.height);tmp_ctx.clearRect(0, 0, canvas.width, canvas.height);, tried $(".sDetails #canvasGraph")[0].reset();
logically, drawCanvas(".sDetails");drawLines(ctx, canvas); should redraw it from scratch but it will not.
Resize the canvas element's width & height and use context.scale to redraw the original drawings at their newly scaled size.
Resizing the canvas element will automatically clear all drawings off the canvas.
Resizing will also automatically reset all context properties back to their default values.
Using context.scale is useful because then the canvas will automatically rescale the original drawings to fit on the newly sized canvas.
Important: Canvas will not automatically redraw the original drawings...you must re-issue the original drawing commands.
Illustration with 2 canvases at same size (their sizes are controlled by range controls)
Illustration with left canvas resized larger
Illustration with right canvas resized larger
Here's example code and a Demo. This demo uses range elements to control the resizing, but you can also do the resizing+redrawing inside window.onresize
var canvas1=document.getElementById("canvas1");
var ctx1=canvas1.getContext("2d");
var canvas2=document.getElementById("canvas2");
var ctx2=canvas2.getContext("2d");
var originalWidth=canvas1.width;
var originalHeight=canvas1.height;
var scale1=1;
var scale2=1;
$myslider1=$('#myslider1');
$myslider1.attr({min:50,max:200}).val(100);
$myslider1.on('input change',function(){
var scale=parseInt($(this).val())/100;
scale1=scale;
redraw(ctx1,scale);
});
$myslider2=$('#myslider2');
$myslider2.attr({min:50,max:200}).val(100);
$myslider2.on('input change',function(){
var scale=parseInt($(this).val())/100;
scale2=scale;
redraw(ctx2,scale);
});
draw(ctx1);
draw(ctx2);
function redraw(ctx,scale){
// Resizing the canvas will clear all drawings off the canvas
// Resizing will also automatically clear the context
// of all its current values and set default context values
ctx.canvas.width=originalWidth*scale;
ctx.canvas.height=originalHeight*scale;
// context.scale will scale the original drawings to fit on
// the newly resized canvas
ctx.scale(scale,scale);
draw(ctx);
// always clean up! Reverse the scale
ctx.scale(-scale,-scale);
}
function draw(ctx){
// note: context.scale causes canvas to do all the rescaling
// math for us, so we can always just draw using the
// original sizes and x,y coordinates
ctx.beginPath();
ctx.moveTo(150,50);
ctx.lineTo(250,150);
ctx.lineTo(50,150);
ctx.closePath();
ctx.stroke();
ctx.fillStyle='skyblue';
ctx.beginPath();
ctx.arc(150,50,20,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(250,150,20,0,Math.PI*2);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath();;
ctx.arc(50,150,20,0,Math.PI*2);
ctx.fill();
ctx.stroke();
}
$("#canvas1, #canvas2").mousemove(function(e){handleMouseMove(e);});
var $mouse=$('#mouse');
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
var bb=e.target.getBoundingClientRect();
mouseX=parseInt(e.clientX-bb.left);
mouseY=parseInt(e.clientY-bb.top);
if(e.target.id=='canvas1'){
$mouse.text('Mouse1: '+mouseX/scale1+' / '+mouseY/scale1+' (scale:'+scale1+')');
}else{
$mouse.text('Mouse2: '+mouseX/scale2+' / '+mouseY/scale2+' (scale:'+scale2+')');
}
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div>Resize left canvas</div>
<input id=myslider1 type=range><br>
<div>Resize right canvas</div>
<input id=myslider2 type=range><br>
<h4 id=mouse>Mouse coordinates:</h4>
<canvas id="canvas1" width=300 height=300></canvas>
<canvas id="canvas2" width=300 height=300></canvas>
If you need scale-independent positions you could use normalized values ([0, 1]) instead and use the size of canvas as the scale factor. This way you can scale and store values without too much concern about the actual target size.
You would also be able to use the mouse positions almost as is and normalize by just dividing them on canvas size.
For example:
When rendering, a point of (1,1) will always draw in lower-right corner as you would do (1 * canvas.width, 1 * canvas.height).
When you store a point you would use the mouse position and divide it on the canvas dimension, for example, if I click in the lower right corner of a canvas of size 400x200, the points would be 400/400 = 1, 200/200 = 1.
Note that width and height would be exclusive (ie. width-1 etc.), but for sake of simplicity...
Example
In this example you can start with any size of the canvas, draw points which are normalized, change size of canvas and have the points redrawn proportionally relative to the original position.
var rng = document.querySelector("input"),
c = document.querySelector("canvas"),
ctx = c.getContext("2d"),
points = [];
// change canvas size and redraw all points
rng.onchange = function() {
c.width = +this.value;
render();
};
// add a new normalized point to array
c.onclick = function(e) {
var r = this.getBoundingClientRect(), // to adjust mouse position
x = e.clientX - r.left,
y = e.clientY - r.top;
points.push({
x: x / c.width, // normalize value to range [0, 1]
y: y / c.height
}); // store point
render(); // redraw (for demo)
};
function render() {
ctx.clearRect(0, 0, c.width, c.height); // clear canvas
ctx.beginPath(); // clear path
for(var i = 0, p; p = points[i]; i++) { // draw points as fixed-size circles
var x = p.x * c.width, // normalized to absolute values
y = p.y * c.height;
ctx.moveTo(x + 5, y);
ctx.arc(x, y, 5, 0, 6.28);
ctx.closePath();
}
ctx.stroke();
}
canvas {background:#ddd}
<h3>Click on canvas to add points, then resize</h3>
<label>Width: <input type="range" min=50 max=600 value=300></label><br>
<canvas></canvas>
I decided to use a scale variable to resize my scales. I resize the canvas canvas.width *= 2; and then I redraw my scales.
var scaleStep;
and use add it into the code: ctx.lineTo(12*24*cScale+12, canvas.height-24); where the scaling needs to be done.
The scaleStep is 2 when maximizing the canvas and 1 when returning to the original size.
I want to achive the following:
Draw a bg-image to the canvas (once or if needed repeatedly)
The image should not be visible at the beginning
While i "paint" shapes to the canvas the bg-image should get visible where the shapes were drawn
The parts of the image that will be revealed shall be "painted" (like with a brush) so i want to use strokes.
What i tried:
- Do not clear the canvas
- Paint rects to the canvas with globalCompositeOperation = 'destination-in'
This works, the rectangles reveal the image but i need strokes
If i use strokes they are ignored with 'destination-in' while i see them with normal globalCompositeOperation.
Is this intended that the strokes are ignored? Is there a workaround like somehow converting the stroke/shape to a bitmap? Or do i have have to use two canvas elements?
In OpenGL i would first draw the image with its rgb values and with a = 0 and then only "paint" the alpha in.
You can solve it by these steps:
Set the image as a pattern
Set the pattern as fillStyle or strokeStyle
When you now fill/stroke your shapes the image will be revealed. Just make sure the initial image fits the area you want to reveal.
Example showing the principle, you should be able to adopt this to your needs:
var ctx = canvas.getContext("2d"),
img = new Image,
radius = 40;
img.onload = setup;
img.src = "http://i.imgur.com/bnAEEXq.jpg";
function setup() {
// set image as pattern for fillStyle
ctx.fillStyle = ctx.createPattern(this, "no-repeat");
// for demo only, reveals image while mousing over canvas
canvas.onmousemove = function(e) {
var r = this.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.arc(x, y, radius, 0, 2*Math.PI);
ctx.fill();
};
}
<canvas id=canvas width=900 height=600></canvas>
Hope this helps!
Alternative solution:
Put the image as a normal image on your website
add a canvas and use CSS positioning to place it right above the image
Fill the canvas with the color you use as the page background
have your paint tools erase the canvas when you draw. By the way, you can set context.globalCompositionOperation = 'destination-out' to turn all drawing operations into an eraser.
Here is an example. As you can see, the alpha properties of your tools are respected.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//prepare canvas
ctx.fillStyle = '#ffffff'
ctx.fillRect(0, 0, 120, 120);
//prepare a 30% opacity eraser
ctx.globalCompositeOperation = 'destination-out';
ctx.lineWidth = 5;
ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)';
// make random strokes around cursor while mouse moves
canvas.onmousemove = function(e) {
var rect = this.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
ctx.beginPath();
ctx.moveTo(x + Math.random() * 33 - 16, y + Math.random() * 33 - 16);
ctx.lineTo(x + Math.random() * 33 - 16, y + Math.random() * 33 - 16);
ctx.stroke();
}
<span>Move your mouse:</span>
<div>
<img src='https://upload.wikimedia.org/wikipedia/commons/thumb/6/61/HTML5_logo_and_wordmark.svg/120px-HTML5_logo_and_wordmark.svg.png' style='position:absolute'>
<canvas id='canvas' width=120 height=120 style='position:absolute'></canvas>
</div>