Canvas Pattern not repeating - javascript

I am making a Canvas Pattern using the following javascript:
//$('#canvas1').height($(document).height());
//console.log($('#canvas1').width($(document).width()));
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
// set up a pattern
var pattern = document.createElement('canvas');
pattern.width = 80;
pattern.height = 80;
var pctx = pattern.getContext('2d');
pctx.beginPath();
pctx.lineWidth = "3";
pctx.strokeStyle = "red";
pctx.rect(74, 74, 3, 3);
pctx.stroke();
var pattern = ctx.createPattern(pattern, "repeat");
ctx.rect(0,0,800,800);
ctx.fillStyle = pattern;
ctx.fill();
The resulting pattern doesn't seem to repeat even when the ctx.rect values are defined as 800. The canvas is as follows:
<canvas id="canvas1"style="position:absolute;left:0;top:0;"></canvas>
How can I make it repeat throughout the height and width of current page. I know that I can set the height and width using jQuery and I tested that, but the pattern is still not repeating. Where am I going wrong?

Well, I guess you got confused with .rect()'s parameters. It should be pctx.rect(3, 3, 74, 74).
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
// set up a pattern
var pattern = document.createElement('canvas');
pattern.width = 80;
pattern.height = 80;
var pctx = pattern.getContext('2d');
pctx.beginPath();
pctx.lineWidth = "3";
pctx.strokeStyle = "red";
pctx.rect(3, 3, 74, 74);
pctx.stroke();
var _pattern = ctx.createPattern(pattern, "repeat");
ctx.rect(0, 0, can.width, can.height);
ctx.fillStyle = _pattern;
ctx.fill();
<canvas id="canvas1" width="320" height="320" style="border: 1px solid black; position: absolute; left: 0; top: 0;"></canvas>

I'm not sure if that's the root of your problem, but when you change the width, you should write pattern.style.width = "80px";
The same applies for height
Let me know if that fixes your problems

Related

Failed to execute 'drawImage' on 'CanvasRenderingContext2D Trying to convert a canvas into image

I am having an issue creating an image of a canvas and translating it onto another canvas. The end goal is to create a loupe magnifier for a cgm viewer/editor, but I'm stuck on this issue. Here is an example.
HTML:
<canvas id="canvas" width=240 height=240 style="background-color: aqua;
left:0;right:0;margin:auto;z-index:1;margin-top:0px;">
</canvas>
<canvas id="canvas1" width=240 height=240 style="background-color:#808080;">
</canvas>
<p></p>
<a id="download" href="" onclick="draw_image();">Download to myImage.jpg</a>
JS:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var ox = canvas.width / 2;
var oy = canvas.height / 2;
ctx.font = "42px serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "#800";
ctx.fillRect(ox / 2, oy / 2, ox, oy);
draw_image = function() {
let canvas1 = document.getElementById('canvas1')
var image = canvas1.toDataURL("image/png")
let ctx1 = canvas1.getContext("2d");
ctx.drawImage(image, 0, 0)
};
You probably wanted to just draw a canvas, no some url
And you wanted to type ctx1 and not ctx
draw_image = function() {
let canvas1 = document.getElementById('canvas1')
let ctx1 = canvas1.getContext("2d");
// just draw the original canvas
ctx1.drawImage(canvas, 0, 0)
};

How can I set the rotation origin for a canvas pattern using a DOMMatrix?

I am using a combination of pattern and DOMMatrix to rotate a texture on a canvas, at the moment the transform origin seems to be set to the x:0 y:0 point on the canvas. I've looked through as much documentation as I can find, but I can't seem to see any way to change this. Is there a way to set the transform origin for a DOMMatrix?
Please note: I specifically don't want to rotate the canvas context if I can avoid it. I am trying to rotate the filled pattern, not the shape it's filling (And in the real version it's a complex shape rather than a rectangle, so working out the "counter rotated" points would add a significant amount complexity that I hope isn't necessary)
Simplified example:
var canvas = document.getElementById('canvas')
var ctx = canvas.getContext('2d')
var rotation = 0
var pattern = null
var matrix = new DOMMatrix()
function draw() {
// Rotate
rotation += 0.15
if (rotation > 360) {
rotation = 0
}
pattern.setTransform(matrix.rotate(rotation))
// Fill
ctx.fillStyle = pattern
ctx.fillRect(10, 10, 150, 80)
// Loop
window.requestAnimationFrame(draw)
}
// Load the image
var img = new Image()
img.src = 'https://picsum.photos/id/1044/100/100'
img.onload = function() {
pattern = ctx.createPattern(img, 'repeat')
ctx.canvas.width = window.innerWidth
ctx.canvas.height = window.innerHeight
window.requestAnimationFrame(draw)
}
<canvas id="canvas"></canvas>
Using a matrix this is usually done by translating the matrix to the desired origin, rotating the matrix and lastly translate it back to the original position. The order is important!
Unfortunately the DOMMatrix isn't well documented. I figured you can do this by chaining the .translate() and .rotate() operations. Since the matrix is suited for 2d and 3d operations the translate method expects x, y and z coordinates.
So for a rotation around the center we need to translate it to the center of the canvas.
Here's an example:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var rotation = 0;
var pattern = null;
var matrix = new DOMMatrix();
var canvas2 = document.getElementById('canvas2');
var ctx2 = canvas2.getContext('2d');
var pattern2 = null;
var matrix2 = new DOMMatrix();
function draw() {
rotation += 0.15
if (rotation > 360) {
rotation = 0
}
pattern.setTransform(matrix.translate(85, 50, 0).rotate(rotation).translate(-85, -50, 0));
ctx.fillStyle = pattern;
ctx.fillRect(10, 10, 150, 80);
pattern2.setTransform(matrix2.rotate(rotation));
ctx2.fillStyle = pattern2;
ctx2.fillRect(10, 10, 150, 80);
window.requestAnimationFrame(draw)
}
// Load the image
var img = new Image();
img.src = 'https://picsum.photos/id/1044/100/100';
img.onload = function() {
pattern = ctx.createPattern(img, 'repeat');
pattern2 = ctx.createPattern(img, 'repeat');
window.requestAnimationFrame(draw);
}
<canvas id="canvas" height="100" width="170" style="float: left;"></canvas>
<canvas id="canvas2" height="100" width="170"></canvas>

How to make putImageData cover the entire canvas

I use getImageData from a canvas and display it on another canvas using putImageData. But it doesn't cover the entire canvas.It displays the picture in its original size.Is there a way to make the cropped picture cover the whole canvas.Thank you in advance.Both canvases are 300 by 300 in size.
var c = document.getElementById("area_c");
var c2 = document.getElementById("area_c2");
var ctx = c.getContext("2d");
var ctx2 = c2.getContext("2d");
var imgData = ctx.getImageData(tx,new_ty ,x, new_y);
ctx2.putImageData(imgData, 0, 0);
In this snippet you can see how to scale another canvas with css (I used a string instead of an image to avoid CORS)
I also added a class if you want to use nearest-neighbor or pixelated
var c = document.getElementById("area_c");
var c2 = document.getElementById("area_c2");
var ctx = c.getContext("2d");
var ctx2 = c2.getContext("2d");
var w = 150;
var h = 70;
c.width = w;
c.height = h;
ctx.font = "50px Arial";
ctx.fillText('Image',0,50);
var x = 5;
var y = 15;
var cw = 60;
var ch = 35;
c2.width = cw;
c2.height = ch;
var scale = 2;
c2.style.width = scale*cw+'px';
c2.style.height = scale*ch+'px';
var imgData = ctx.getImageData(x,y,cw,ch);
ctx2.putImageData(imgData, 0, 0);
document.querySelector('input[name=pixelate]').addEventListener('change',()=>{c2.classList.toggle('pixelate')});
canvas {
border: 1px solid #333;
}
.pixelate {
image-rendering: auto;
image-rendering: crisp-edges;
image-rendering: pixelated;
}
<canvas id="area_c"></canvas>
<canvas id="area_c2"></canvas>
<input name="pixelate" type="checkbox"><label for="pixelate">Pixelate</label>

Adding Image to an Object with Javascript Canvas [duplicate]

I am trying to move an image from the right to the center and I am not sure if this is the best way.
var imgTag = null;
var x = 0;
var y = 0;
var id;
function doCanvas()
{
var canvas = document.getElementById('icanvas');
var ctx = canvas.getContext("2d");
var imgBkg = document.getElementById('imgBkg');
imgTag = document.getElementById('imgTag');
ctx.drawImage(imgBkg, 0, 0);
x = canvas.width;
y = 40;
id = setInterval(moveImg, 0.25);
}
function moveImg()
{
if(x <= 250)
clearInterval(id);
var canvas = document.getElementById('icanvas');
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
var imgBkg = document.getElementById('imgBkg');
ctx.drawImage(imgBkg, 0, 0);
ctx.drawImage(imgTag, x, y);
x = x - 1;
}
Any advice?
This question is 5 years old, but since we now have requestAnimationFrame() method, here's an approach for that using vanilla JavaScript:
var imgTag = new Image(),
canvas = document.getElementById('icanvas'),
ctx = canvas.getContext("2d"),
x = canvas.width,
y = 0;
imgTag.onload = animate;
imgTag.src = "http://i.stack.imgur.com/Rk0DW.png"; // load image
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
ctx.drawImage(imgTag, x, y); // draw image at current position
x -= 4;
if (x > 250) requestAnimationFrame(animate) // loop
}
<canvas id="icanvas" width=640 height=180></canvas>
drawImage() enables to define which part of the source image to draw on target canvas. I would suggest for each moveImg() calculate the previous image position, overwrite the previous image with that part of imgBkg, then draw the new image. Supposedly this will save some computing power.
Here's my answer.
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var myImg = new Image();
var myImgPos = {
x: 250,
y: 125,
width: 50,
height: 25
}
function draw() {
myImg.onload = function() {
ctx.drawImage(myImg, myImgPos.x, myImgPos.y, myImgPos.width, myImgPos.height);
}
myImg.src = "https://mario.wiki.gallery/images/thumb/c/cc/NSMBUD_Mariojump.png/1200px-NSMBUD_Mariojump.png";
}
function moveMyImg() {
ctx.clearRect(myImgPos.x, myImgPos.y, myImgPos.x + myImgPos.width, myImgPos.y +
myImgPos.height);
myImgPos.x -= 5;
}
setInterval(draw, 50);
setInterval(moveMyImg, 50);
<canvas id="canvas" class="canvas" width="250" height="150"></canvas>
For lag free animations,i generally use kinetic.js.
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var hexagon = new Kinetic.RegularPolygon({
x: stage.width()/2,
y: stage.height()/2,
sides: 6,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
layer.add(hexagon);
stage.add(layer);
var amplitude = 150;
var period = 2000;
// in ms
var centerX = stage.width()/2;
var anim = new Kinetic.Animation(function(frame) {
hexagon.setX(amplitude * Math.sin(frame.time * 2 * Math.PI / period) + centerX);
}, layer);
anim.start();
Here's the example,if you wanna take a look.
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-animate-position-tutorial/
Why i suggest this is because,setInterval or setTimeout a particular function causes issues when large amount of simultaneous animations take place,but kinetic.Animation deals with framerates more intelligently.
Explaining window.requestAnimationFrame() with an example
In the following snippet I'm using an image for the piece that is going to be animated.
I'll be honest... window.requestAnimationFrame() wasn't easy for me to understand, that is why I coded it as clear and intuitive as possible. So that you may struggle less than I did to get my head around it.
const
canvas = document.getElementById('root'),
btn = document.getElementById('btn'),
ctx = canvas.getContext('2d'),
brickImage = new Image(),
piece = {image: brickImage, x:400, y:70, width:70};
brickImage.src = "https://i.stack.imgur.com/YreH6.png";
// When btn is clicked execute start()
btn.addEventListener('click', start)
function start(){
btn.value = 'animation started'
// Start gameLoop()
brickImage.onload = window.requestAnimationFrame(gameLoop)
}
function gameLoop(){
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Draw at coordinates x and y
ctx.drawImage(piece.image, piece.x, piece.y)
let pieceLeftSidePos = piece.x;
let middlePos = canvas.width/2 - piece.width/2;
// Brick stops when it gets to the middle of the canvas
if(pieceLeftSidePos > middlePos) piece.x -= 2;
window.requestAnimationFrame(gameLoop) // Needed to keep looping
}
<input id="btn" type="button" value="start" />
<p>
<canvas id="root" width="400" style="border:1px solid grey">
A key point
Inside the start() function we have:
brickImage.onload = window.requestAnimationFrame(gameLoop);
This could also be written like: window.requestAnimationFrame(gameLoop);
and it would probably work, but I'm adding the brickImage.onload to make sure that the image has loaded first. If not it could cause some issues.
Note: window.requestAnimationFrame() usually loops at 60 times per second.

HTML5 Canvas Text Stroke gives unwanted overlapping in chrome but works fine in Safari

Basic text stroke code.
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = 80;
var y = 110;
context.font = '100pt arial';
context.lineWidth = 37;
context.strokeStyle = "rgba(255,0,0,0.5)";
context.strokeText('Ho', x, y);
</script>
Output in Chrome and Firefox is :
The overlapping area between 'H' and 'o' is darker than non overlapping area.
I want to have same opacity/alpha for the stroke text.
I tried it in Safari on Mac system and there was not such a problem. In safari i got uniform opacity/alpha for both overlapping and non-overlapping region.
I need to understand why this is happening in chrome and what should i do to have uniform opacity/alpha.
The behaviour is not what is to be expected, the strokeText call one would expect to be part of a single path, and would consider it a bug in both Chrome and Firefox.
Nevertheless you need a solution. The only one I can think of is to create a second canvas. Render the text without opacity on that canvas then render that canvas onto the original canvas using ctx.globalAlpha = 0.5
Example.
var canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 500;
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
var x = 40;
var y = 110;
ctx.font = '100pt arial';
ctx.lineWidth = 20;
ctx.fillRect(10,y,500,10);
ctx.strokeStyle = "rgba(255,0,0,0.5)";
ctx.strokeText('Bad', x, y);
var workCan = document.createElement("canvas");
workCan.width = canvas.width;
workCan.height = canvas.height;
var ctx1 = workCan.getContext("2d");
ctx1.font = '100pt arial';
ctx1.lineWidth = 20;
ctx1.strokeStyle = "rgba(255,0,0,1)";
x = 40;
y = 260;
ctx1.strokeText('Good', x, y);
ctx.fillRect(10,y,500,10);
ctx.globalAlpha = 0.5;
ctx.drawImage(workCan,0,0);
You can use plain CSS gradient with alpha values, and the alpha will not add up:
background: -webkit-linear-gradient(top, rgba(10, 14, 15, 0.3), rgba(10, 14, 15, 0.3));
-webkit-background-clip: text;
-webkit-text-stroke: 1vw transparent;

Categories

Resources