Related
Sorry if the title is vague, it was hard to figure out what to call this problem. I have a canvas and an image overlaying each other like this:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
function newLine(){
let value = (Math.floor(Math.random() * 100) + 1) * 0.06283185307179587;
ctx.clearRect(0, 0, c.width, c.height);
ctx.lineWidth = 10;
ctx.strokeStyle = '#00FF00';
ctx.beginPath();
ctx.arc(100, 75, 55, 0, value);
ctx.stroke();
}
setInterval(()=>{
newLine()
}, 100)
img{
width: 100px;
border-radius: 50px;
position: absolute;
left: 58px;
top: 33px;
}
<img src="https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s"></img>
<canvas id="myCanvas"></canvas>
This works, but when I go to add multiple, or add a div above them, because of the image's absolute position, the image ends up over the div, not the canvas. Here's an example illustrating my issues:
var can = document.getElementsByClassName("canvas");
for (var i = 0; i < can.length; i++) {
let c = can.item(i)
var ctx = c.getContext("2d");
function newLine(){
let value = (Math.floor(Math.random() * 100) + 1) * 0.06283185307179587;
ctx.clearRect(0, 0, c.width, c.height);
ctx.lineWidth = 10;
ctx.strokeStyle = '#00FF00';
ctx.beginPath();
ctx.arc(100, 75, 55, 0, value);
ctx.stroke();
requestAnimationFrame(newLine)
}
newLine()
}
img{
width: 100px;
border-radius: 50px;
position: absolute;
left: 58px;
top: 33px;
}
div{
background-color: red;
}
canvas{
display: inline-block;
}
<div class="top">
<h1>Some text</h1>
</div>
<img src="https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s"></img>
<canvas class="canvas"></canvas>
<img src="https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s"></img>
<canvas class="canvas"></canvas>
(If you could figure out why the first one doesn't play, that would be amazing also)
The issues are:
The first canvas doesn't play
The images (because it's position is absolute) are stacked up and not over the second canvas
Is it possible to do this but with relative positioning? Please let me know if this was a confusing question. Thanks
Avoid mixing images <img> and canvas, instead just draw the image inside the canvas with drawImage nothing special just a few more lines on JavaScript
Here is an example:
(And I also fixed your issue where the first one does not play)
class Shape {
constructor(ctx, width, height, image) {
this.ctx = ctx;
this.width = width;
this.height = height;
this.image = image;
this.value = Math.random() * 5
this.inc = 0.03
}
draw() {
this.value += this.inc;
if (this.value > 2 * Math.PI || this.value < 0.05) this.inc *= -1;
this.ctx.clearRect(0, 0, this.width, this.height);
// Draw centered round image
this.ctx.beginPath();
this.ctx.drawImage(this.image , 50, 24, 100, 100);
this.ctx.lineWidth = 50;
this.ctx.strokeStyle = 'white';
this.ctx.arc(100, 75, 75, 0, 2 * Math.PI);
this.ctx.stroke();
// Draw green progress bar
this.ctx.beginPath();
this.ctx.lineWidth = 12;
this.ctx.strokeStyle = '#00FF00';
this.ctx.arc(100, 75, 55, 0, this.value);
this.ctx.stroke();
}
}
var can = document.getElementsByClassName("canvas");
var animations = []
function loop() {
animations.forEach(anim => anim.draw());
requestAnimationFrame(loop)
}
var image = new Image();
image.src = "https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s";
image.onload = imageLoaded;
function imageLoaded() {
for (var i = 0; i < can.length; i++) {
let c = can.item(i)
animations.push(new Shape(c.getContext("2d"), c.width, c.height, image));
}
loop()
}
div {
background-color: red;
}
canvas {
display: inline-block;
border: 1px solid gray;
}
<div class="top">
<h2>Some text</h2>
</div>
<canvas class="canvas" width=200></canvas>
<canvas class="canvas" width=200></canvas>
There are two problems: positioning of the images above the canvases and only one canvas playing.
First positioning. The img is given position absolute. To find out where to position it the system goes back up the tree to find the first element it comes across that has a position set. It then positions the img relative to that.
The canvas is at the same level as the img so that doesn't influence the img's position, going back up the first thing it hits with a position is the body element (which is positioned by default) and so it puts the img slightly set off from the top of the page.
To remedy this we can put each pair of img/canvas into their own div and position that inline-block. The img will then be positioned relative to that div and so will be placed over the canvas as required.
Second only one canvas playing. The code within the newline function uses a c and ctx. These are not set within the function so it takes the last one that was set. In addition, requestAnimationFrame with its callback is called for each can.length times each time so you end up with more than one of these. They stack up. The browser gets 'confused' and (at least on my laptop) it ended up not responding.
To cure this, call requestAnimationFrame just once per 'cycle' and repaint all the canvases at the same call to the callback - so put the definition of c and ctx inside the for loop so every canvas is set up on each frame.
var can = document.getElementsByClassName("canvas");
function newLine(){
for (var i = 0; i < can.length; i++) {
let c = can.item(i)
var ctx = c.getContext("2d");
let value = (Math.floor(Math.random() * 100) + 1) * 0.06283185307179587;
ctx.clearRect(0, 0, c.width, c.height);
ctx.lineWidth = 10;
ctx.strokeStyle = '#00FF00';
ctx.beginPath();
ctx.arc(100, 75, 55, 0, value);
ctx.stroke();
}
requestAnimationFrame(newLine)
}
newLine()
img{
width: 100px;
border-radius: 50px;
position: absolute;
left: 58px;
top: 33px;
}
div{
background-color: red;
}
canvas{
display: inline-block;
}
.wrapper {
display: inline-block;
position: relative;
}
<div class="top">
<h1>Some text</h1>
</div>
<div class="wrapper">
<!-- <img src="https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s"></img>-->
<img src="https://i.stack.imgur.com/ZBclx.jpg" />
<canvas class="canvas"></canvas>
</div>
<div class="wrapper">
<!-- <img src="https://media-exp1.licdn.com/dms/image/C4E0BAQHikN6EXPd23Q/company-logo_200_200/0/1595359131127?e=2159024400&v=beta&t=S5MNjBDjiH433VCWzjPeiopNDhxGwmfcMk4Zf1P_m_s"></img>-->
<img src="https://i.stack.imgur.com/ZBclx.jpg" />
<canvas class="canvas"></canvas>
</div>
Note, the actual logo image didn't show on your snippet - I guess it cannot be downloaded by just anybody - so I put it on imgur. The positioning is slightly off because the top and left positions had been previously set to compensate for its being positioned relative to the body. These need a bit of adjustment.
There is quite a lot of GPU being used (around 20% on my laptop, obviously that will differ depending on the user's equipment). It might be worth investigating whether a CSS animation could be used instead of having to redraw several canvases on each frame.
The animated canvases are very flickery. I don't know whether this is intended or whether it was just a test, but they may be upsetting for those with conditions such as epilepsy.
My website for testing gives me an error when I try to load my webpage. I'm using TypeScript-Compile to compile it and it gives me an error with no explanation, and points to code in the typescript-compile source code.
http://hazelpy.ml/ is the website if you want to check the logs.
Here is the HTML source:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>TypeScript</title>
<style media="screen">
#GameCanvas {
border: 2px solid black;
border-radius: 1px;
}
</style>
</head>
<body>
<canvas id="GameCanvas" width="1080" height="720"></canvas>
<script type="text/typescript" charset="utf-8">
const CVS = document.getElementById("GameCanvas");
const CTX = CVS.getContext("2d");
class GameObject {
position;
color: string;
width: number;
height: number;
constructor (position, color, width, height) {
this.position = position;
this.color = color;
this.width = width;
this.height = height;
}
fill(Context) {
Context.save();
Context.fillStyle = this.color;
Context.fill(this.position[0], this.position[1], this.width, this.height);
Context.restore();
}
}
function EndGame() {
return false;
}
var GameRunning: boolean = true;
const objects: GameObject[] = [];
objects.push(new GameObject([0, 0], "rgb(255, 255, 0)", 10, 10));
while (GameRunning) {
CTX.clearRect(0, 0, 1080, 720);
for (var i = 0; i < objects.length; i++) {
var Selected: GameObject = objects[i];
Selected.fill(CTX);
}
}
</script>
</body>
<script type="text/javascript" src="./source/typescript.min.js"></script>
<script type="text/javascript" src="./source/typescript.compile.min.js"></script>
</html>
Yes, I have tried moving the two typescript compile scripts to before the tag. Did nothing.
The problem seems to be in the typescript-compile min file on the third to last line. I wouldn't be able to tell where the problem is though.
Turns out the error was in my code where I said:
const objects: GameObject[] = [];
I hadn't noticed at the moment that using const in this scenario was not the right choice. Changing this to a var fixed the issue.
<!DOCTYPE html>
<html>
<head>
<title>Breakout</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<canvas width="900" height="450" class="canvas"></canvas>
<script src="scripts/base.js"></script>
</body>
</html>
This is the index file
*{
margin: 0;
padding: 0;
}
.canvas{
background-color: #b7b7b7;
}
This is the CSS file
var canvas = document.getElementsByClassName('canvas');
var context = canvas.getContext('2d');
context.beginPath();
context.drawRect(20,30,50,40);
context.fillStyle("#0022ff");
context.fill();
context.endPath();
And the javascript file.
I am trying to create a breakout game and I am following a tutorial from udemy. Unfortunately, it seems there is something wrong with this code, but I don't know what. I verified the code one thousand times and I haven't found anything.
That's because in your var canvas, you're calling document.getElementByClassName which will return an "array-like" object. So, I'd suggest you to use IDs instead of selecting using a class.
var context = canvas.getContext('2d'); should be var context = canvas[0].getContext('2d'); because you're using document.getElementsByClassName which will return a collection of all the elements with that class name. and you want the context of the first one.
context.drawRect should be context.rect.
context.fillStyle is not a function it should be context.fillStyle = "#0022ff";
context.endPath(); should be context.closePath();
In your case you don't need context.beginPath(); and context.closePath();. context.rect already creates the path.
var canvas = document.getElementsByClassName('canvas');
var context = canvas[0].getContext('2d');
context.rect(20, 30, 50, 40);
context.fillStyle = "#0022ff";
context.fill();
* {
margin: 0;
padding: 0;
}
.canvas {
background-color: #b7b7b7;
}
<canvas width="900" height="450" class="canvas"></canvas>
Use fillRect instead of drawRect:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.fillRect(20,30,50,40);
ctx.endPath();
</script>
I'm using the following code to generate a Pythagoras fractal tree using HTML5 canvas element:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script src="jquery.js" type="text/javascript"></script>
<style>
#sketch
{
border: 1px solid black;
}
</style>
<script type="text/javascript">
window.onload = function()
{
var canvas = document.getElementById("sketch");
var context = canvas.getContext("2d");
context.fillStyle = "rgba(0, 0, 200, 0.5)";
context.beginPath();
context.moveTo(450,550);
context.lineTo(450,450);
context.lineTo(550,450);
context.lineTo(550,550);
context.fill();
fractal(context,[450,550],[450,450],[550,450],[550,550],5);
};
function fractal(context,P1,P2,P3,P4,depth)
{
context.fillStyle = "rgba(0,0,200,"+(depth/8).toString()+")";
context.save();
if(depth < 0)
{
return null;
}
/*Find C*/
C = divide(add(divide(add(P1,P2),2),divide(add(P3,P4),2)),2);
var V1 = divide(minus(C,P1),length(C,P1));
var V2 = divide(minus(C,P4),length(C,P4));
var P6 = add(P2,multiply(V2,length(P1,P2)/Math.sqrt(2)));
var P7 = add(P6,multiply(V1,length(P1,P2)/Math.sqrt(2)));
var P5 = add(P2,multiply(V1,length(P1,P2)/Math.sqrt(2)));
var P9 = add(P3,multiply(V1,length(P1,P2)/Math.sqrt(2)));
var P8 = add(P9,multiply(V2,length(P1,P2)/Math.sqrt(2)));
context.moveTo(P2[0],P2[1]);
context.lineTo(P6[0],P6[1]);
context.lineTo(P7[0],P7[1]);
context.lineTo(P5[0],P5[1]);
context.fill();
context.moveTo(P5[0],P5[1]);
context.lineTo(P8[0],P8[1]);
context.lineTo(P9[0],P9[1]);
context.lineTo(P3[0],P3[1]);
context.fill();
fractal(context,P2,P6,P7,P5,depth-1);
fractal(context,P5,P8,P9,P3,depth-1);
}
function multiply(v, num){
return [v[0]*num, v[1]*num];
}
function divide(v, num){
return [v[0]/num, v[1]/num];
}
function add(a, b){
return [a[0]+b[0], a[1]+b[1]];
}
function minus(a, b){
return [a[0]-b[0], a[1]-b[1]];
}
function length(a, b){
return Math.sqrt(Math.pow(a[0] - b[0],2) +
Math.pow(a[1] - b[1],2));
}
</script>
<title>Square</title>
</head>
<body>
<canvas id="sketch" height="1000" width="1000"></canvas>
</body>
</html>
I'm changing the opacity value with every iteration. But I don't see it in the result.
How can this be fixed??
You were really close to the right solution. Look here:
http://jsfiddle.net/mbessey/Wj4VH/
The key difference here is calling beginPath() before starting the moveTo() and lineTo() for each square. So, instead of:
context.moveTo(P2[0],P2[1]);
context.lineTo(P6[0],P6[1]);
context.lineTo(P7[0],P7[1]);
context.lineTo(P5[0],P5[1]);
context.fill();
context.moveTo(P5[0],P5[1]);
context.lineTo(P8[0],P8[1]);
context.lineTo(P9[0],P9[1]);
context.lineTo(P3[0],P3[1]);
context.fill();
You want:
context.beginPath()
context.moveTo(P2[0],P2[1]);
context.lineTo(P6[0],P6[1]);
context.lineTo(P7[0],P7[1]);
context.lineTo(P5[0],P5[1]);
context.fill();
context.beginPath()
context.moveTo(P5[0],P5[1]);
context.lineTo(P8[0],P8[1]);
context.lineTo(P9[0],P9[1]);
context.lineTo(P3[0],P3[1]);
context.fill();
What you were doing was essentially creating one large path with all the squares in it and then filling it all with the same color.
Check This I have changed the opacity of the canvas itself ,making it simple and easy to achieve the target.
Adding rgba(0, 0, 200, 1) and
#sketch
{
border: 1px solid black;
opacity: 0.3;
}
will suffice what you want
Can I use the canvas element as a css background?
This has been possible in WebKit since 2008, see here.
<html>
<head>
<style>
div { background: -webkit-canvas(squares); width:600px; height:600px; border:2px solid black }
</style>
<script type="application/x-javascript">
function draw(w, h) {
var ctx = document.getCSSCanvasContext("2d", "squares", w, h);
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, 55, 50);
}
</script>
</head>
<body onload="draw(300, 300)">
<div></div>
</body>
</html>
Currently, Firefox 4 contains a feature, which allows you to use any element (including canvas) as a CSS background, in this fashion:
<p id="myBackground1" style="background: darkorange; color: white; width: 300px; height: 40px;">
This element will be used as a background.
</p>
<p style="background: -moz-element(#myBackground1); padding: 20px 10px; font-weight: bold;">
This box uses #myBackground1 as its background!
</p>
See Mozilla hacks for specifics.
Yes!!!! You can put a canvas in CSS background.
var Canvas = document.createElement("canvas");
... do your canvas drawing....
$('body').css({'background-image':"url(" + Canvas.toDataURL("image/png")+ ")" });
I know this is a pretty old question but I felt like posting my answer for people who'd visit this page because this is the correct answer, in just one line of code, using the .toDataURL function. It works in every browser that supports canvas.
I think the closest you could get is to render into a canvas, call toDataUrl() on it to retrieve the contents as an image, and assignment that result to the desired element's background-image property. This will only give a static background, though. If you want to be able to further update the canvas, however, then you'll need to instead position the canvas behind another element, as Johan has already suggested.
I've been triying to achieve this same feature past weeks, the best solution I've found its the same proposed by bcat:
Render canvas (visible or hidden)
Get canvas image with "canvas.toDataURL"
Asign this image-data as background image for the element (I use MooTools)
The bad news, for static images works great, but with animation in Chrome sometimes "blinks", and in Firefox blinks-a-lot. Maybe someone knows a workaround to get rid of this "nasty blinking".
Best regards.
P:.
<!DOCTYPE html>
<html>
<head>
<title>Asign canvas to element background</title>
<script type="text/javascript" src="/js/mootools.1.2.4.js"></script>
<style type="text/css">
* {
outline:0;
padding:0;
margin:0;
border:0;
}
body {
color:#fff;
background:#242424;
}
</style>
<script>
window.addEvent('domready',function() {
//GET BODY
var mibodi = $('mibodi');
var viewportSize = mibodi.getSize();
//GET CANVAS
var micanvas = $('micanvas');
var ctx = micanvas.getContext('2d');
var playAnimation = true;
//GET DIV
var midiv = $('midiv');
//VARIABLES
var rotate_angle = 0;
var rotate_angle_inc = 0.05;
//FUNCIÓN DE INICIALIZACIÓN
function init(){
ctx.clearRect (0, 0, 512, 512); //CLEAR CANVAS
ctx.fillStyle = 'rgba(128,128,128,1)';
ctx.strokeStyle = 'rgba(255,255,255,1)';
if (playAnimation) {
setInterval(draw,100);//
}
} //INIT
//FUNCIÓN DE DIBUJADO
function draw() {
//CLEAR BACKGROUND
ctx.clearRect (0, 0, 512, 512);
//DRAW ROTATING RECTANGLE
ctx.save();
ctx.translate( micanvas.width / 2, micanvas.height / 2 );
ctx.rotate( rotate_angle );
ctx.fillRect(0, 0, 100, 100);
ctx.restore();
//GET CANVAS IMAGE
var dataURL = micanvas.toDataURL("image/png");
//SET IMAGE AS BACKGROUND OF THE ELEMENTS
midiv.setStyle('background-image', 'url(' + dataURL + ')');
mibodi.setStyle('background-image', 'url(' + dataURL + ')');
//ANGLE INCREMENT
rotate_angle = rotate_angle + rotate_angle_inc;
} //DRAW
//BEGIN TO DRAW
init();
});//domeady
</script>
</head>
<body id="mibodi" >
<canvas id="micanvas" width="512" height="512" style="float:left;" style="display:none;">
Este texto se muestra para los navegadores no compatibles con canvas.
<br>
Por favor, utiliza Firefox, Chrome, Safari u Opera.
</canvas>
<div id="midiv" style="width:512px;height:512px;background:#f00;float:left;">
Sample
</div>
</body>
</html>
Try -moz-element(#id) for CSS background in Firefox.
And -webkit-canvas(name) for CSS background in WebKit based browsers.
You can use CSS Paint API
.elem {
backgound: paint(squares);
}
See more details here:
Blog posts:
https://vitaliy-bobrov.github.io/blog/exploring-the-css-paint-api/
https://vitaliy-bobrov.github.io/blog/css-paint-in-action-bar-chart/
Demos: https://vitaliy-bobrov.github.io/css-paint-demos/
You can emulate this behavior quickly without the performance drop of toDataURL() using z-index (granted, it's a workaround, since CSS images 4 / CSS Houdini hasn't implemented "background: element(#mycanvas)" as of 2017))
Working JSFiddle here. I didn't write this, all credit goes to Derek Leung:
http://jsfiddle.net/DerekL/uw5XU/
Unable to comment so I will create my own answer for this.
This answer is based off of #livedo, #Eric Rowell, and #shabunc
http://jsfiddle.net/MDooley47/yj26psdb/
window.i = 0;
function draw(w, h) {
window.i+=5;
if (window.webkitURL != null) {
var ctx = document.getCSSCanvasContext("2d", "squares", 100, 100);
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, w, h);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, w, h);
}
else {
var ctxmozc = document.getElementById("squares");
var ctxmoz = ctxmozc.getContext("2d");
ctxmoz.fillStyle = "rgb(200,0,0)";
ctxmoz.fillRect (10, 10, w, h);
ctxmoz.fillStyle = "rgba(0, 0, 200, 0.5)";
ctxmoz.fillRect (30, 30, w, h);
}
}
setInterval(function(){draw(window.i, window.i);}, 500);
div {
background: -webkit-canvas(squares);
background: -moz-element(#squares) repeat-x;
width:575px;
height:475px;
border:2px solid black
}
<body>
<div></div>
<canvas id="squares" name="squaresmoz" style="display: none;" ></canvas>
</body>