I found some code in CodePen.io that produces a "matrix" effect and I want to try to integrate it on my website. This is the code: https://codepen.io/gnsp/pen/vYBQZJm .
What I am trying to do is to use this script as the background for my one-page website: I want this to be shown behind text and image blocks instead of a picture or a color background.
The page builder I am using is GoodLayers and it allows a "code" block which places a block that you can write javascript in which then gets interpreted.
This is the code:
<style> body {
margin: 0;
height: 100vh;
width: 100vw;
} </style>
<div>
<canvas id= "canv" height:window.innerHeight width:window.innerWidth style="z-index=0"> </canvas>
</div>
<script>
const canvas = document.getElementById('canv');
const ctx = canvas.getContext('2d');
const w = canvas.width = document.body.offsetWidth;
const h = canvas.height = document.body.offsetHeight;
const cols = Math.floor(w / 20) + 1;
const ypos = Array(cols).fill(0);
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, w, h);
function matrix () {
ctx.fillStyle = '#0001';
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = '#0f0';
ctx.font = '15pt monospace';
ypos.forEach((y, ind) => {
const text = String.fromCharCode(70,105,68,105) ;
const x = ind * 20;
ctx.fillText(text, x, y);
if (y > 100 + Math.random() * 10000) ypos[ind] = 0;
else ypos[ind] = y + 20;
});
}
setInterval(matrix, 50);
</script>
I have placed it inside a section wrapper.
This is how it looks in the backend.
And this is how it looks in the frontend: not centered and appears as a block that does not overlap with anything.
That's how it looks when I scroll down.
I want for it to either move with the viewport or better- to be fixed and just cover the whole page from top to bottom.
I am extremely new to this and hardly ever hardcoded anything in CSS or JS without the use of UI of various page builders so please understand that.
I am drawing an emoji on a <canvas> element using the fillText method of the 2D context, and right after I am using getImageData to get the image as an array, like so :
ctx.fillText('🤖', 500, 500)
const imageData = ctx.getImageData(0, 0, 1000, 1000)
This works without any issue on firefox and iOS, but for some reason, imageData comes out empty on Chrome (Chromium 75.0.3770.90) when the font size is too big. See the following snippet :
https://codepen.io/anon/pen/OKWMBb?editors=1111
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<canvas id="c1" width="1000px" height="1000px"></canvas>
<canvas id="c2" width="1000px" height="1000px"></canvas>
<canvas id="c3" width="1000px" height="1000px"></canvas>
<script>
var c1 = document.querySelector('#c1')
var c2 = document.querySelector('#c2')
var c3 = document.querySelector('#c3')
var ctx1 = c1.getContext('2d')
var ctx2 = c2.getContext('2d')
var ctx3 = c3.getContext('2d')
ctx1.font = '500px monospace'
ctx2.font = '500px monospace'
ctx3.font = '200px monospace'
ctx1.fillText('🤖', 500, 500)
ctx2.fillText('🤖', 500, 500)
ctx3.fillText('🤖', 500, 500)
function printImageData(ctx, canvasId) {
const imageData1 = ctx.getImageData(0, 0, 1000, 1000)
console.log(`${canvasId} has data : `, !imageData1.data.every((v) => v === 0))
}
setTimeout(() => printImageData(ctx1, '#c1'), 100)
printImageData(ctx2, '#c2')
printImageData(ctx3, '#c3')
// Chrome prints :
// #c2 has data : false
// #c3 has data : true
// #c1 has data : true
</script>
</body>
</html>
I suspect this has to do with rendering time for the big emoji, but I can't find any reference of this anywhere, nor any workaround (besides the not-very robust setTimeout hack).
That's indeed a weird bug, very probably in getImageData, drawImage is not affected.
So one trick to workaround that issue is to call ctx.drawImage(ctx.canvas, 0,0); before getting the image data:
var c1 = document.querySelector('#c1');
var c2 = document.querySelector('#c2');
var ctx1 = c1.getContext('2d');
var ctx2 = c2.getContext('2d');
ctx1.font = '500px monospace';
ctx2.font = '500px monospace';
ctx1.fillText('🤖', 500, 500);
ctx2.fillText('🤖', 500, 500);
function printImageData(ctx, canvasId) {
const imageData1 = ctx.getImageData(0, 0, 1000, 1000);
console.log(`${canvasId} has data : `, !imageData1.data.every((v) => v === 0));
}
// #c1 has no workaround applied
printImageData(ctx1, '#c1');
// #c2 has the workaround applied
ctx2.globalCompositeOperation = "copy";
ctx2.drawImage(ctx2.canvas, 0, 0);
ctx2.globalCompositeOperation = "source-over";
printImageData(ctx2, '#c2');
<canvas id="c1" width="1000px" height="1000px"></canvas>
<canvas id="c2" width="1000px" height="1000px"></canvas>
After further tests, it seems the problem is that these emojis can't be drawn by software only when the font-size is bigger than 256px (at least when I disable Hardware acceleration, they're just not rendered at all). Thus I guess *getImageData* is somehow forcing software rendering, and making it fail even when HW acceleration is turned on.
I opened this issue on chromium's bug-tracker, but note that your particular case with HWA on is actually already fixed in canary version 78.
UPDATE
After some more test it seams there is a problem
This is not expected behavior and is a BUG with Chromes rendering.
The rest is the original answer before I found that bug with updates marked.
Alignment?
I dont see any problem Chrome 75.0.3770.142
However it could be that the font is just offset and thus missing the canvas.
Make sure you have set the text alignments as your example is just on the canvas on the right side.
ctx.textAlign = "center";
ctx.textBaseline = "middle";
Scale via transform
If this still does not work you can scale the font using the 2D transform
Example
// set constants
const fontSize = 500; // Size you want
const usingFontSize = 100; // size of font you are using
const scaleFontBy = fontSize / usingFontSize; // calculates scale
const [x, y] = [500, 500]; // where to draw text
// set 2D state
ctx.font = usingFontSize + "px monospace"
ctx.textAlign = "center"; // ensure rendering is centered
ctx.textBaseline = "middle";
ctx.setTransform(scaleFontBy, 0, 0, scaleFontBy, x, y);
// render content
ctx.fillText('🤖', 0, 0); // Draw at center of transformed space
// Restore transform state to default
ctx.setTransform(1,0,0,1,0,0);
Updated Demo
Update Will log error when can not get pixel of rendered font.
To test it out the following example draws font 50 to 2500pixels (or more if you want).
requestAnimationFrame(renderLoop);
const ctx = canvas.getContext("2d");
var w,h, x, y;
const usingFontSize = 64; // size of font you are using
const fontSizeMax = 2500; // Max Size you want
const fontSizeMin = 50; // Min Size you want
const text = "😀,😁,😂,😃,😄,😅,😆,😇,😉,😊,😋,😌,😍,😎,😏,😐,😑,😒,😓,😔,😕,😖,😗,😘,😙,😚,😛,😜,😝,😞,😟,😠,👹,👺,👻,👼,🚜,👾,👿,💀".split(",");
function draw(text,fontSize) {
if (innerHeight !== canvas.height) {
// resize clears state so must set font and alignment
h = canvas.height = innerHeight;
w = canvas.width = innerWidth;
ctx.font = usingFontSize + "px monospace"
ctx.textAlign = "center"; // ensure rendering is centered
ctx.textBaseline = "middle";
ctx.lineWidth = 5;
ctx.lineJoin = "round";
ctx.strokeStyle = "white";
x = w / 2;
y = h / 2;
}else{
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,w,h);
}
const scaleFontBy = fontSize / usingFontSize; // calculates scale
ctx.setTransform(scaleFontBy, 0, 0, scaleFontBy, x, y);
// render content
ctx.fillText(text, 0, 0); // Draw at center of transformed space
const isRendered = ctx.getImageData(x | 0, y | 0, 1, 1).data[3];
if(!isRendered) {console.clear(); console.error("Bad font render at size " + (usingFontSize * scaleFontBy | 0) + "px") }
ctx.setTransform(1,0,0,1,x, 40);
ctx.strokeText("Font size " + (usingFontSize * scaleFontBy | 0) + "px", 0, 0);
ctx.fillText("Font size " + (usingFontSize * scaleFontBy | 0) + "px", 0, 0);
}
function renderLoop(time) {
draw(text[(time / 2000 | 0) % text.length], (Math.sin(time * Math.PI / 1000 - Math.PI / 2) * 0.5 + 0.5) ** 2 * (fontSizeMax - fontSizeMin) + fontSizeMin);
requestAnimationFrame(renderLoop);
}
body {
padding: 0px;
}
canvas {
position: absolute;
top: 0px;
left: 0px;
}
<canvas id="canvas"></canvas>
Still not fixed
If this does not solve the problem then it is likely a Chrome bug related to your system. It works for me on Win 10 32 and 64 bit systems running Chrome 75.0.3770.142
BTW
You say
"I suspect this has to do with rendering time for the big emoji, ... besides the not-very robust setTimeout hack ..."
2D rendering calls are blocking. They will not execute the next line of code until they have completed rendering. You never need to use a timeout.
Hope this helps
😀
update
😕
I attempted to animate a sprite sheet using html and javascript to no avail. Here is my sprite sheet
Below is lines 36-59 of my code. I'm not getting any errors so I don't really know what's wrong. How do I fix/improve my code?
This is for a project I'm doing. I've tried using different methods I've found online but none really worked either. I've tried shifting the image as well.
<html>
<head>
<title>Tree Animation</title>
</head>
<body>
<canvas id='canvas'></canvas>
<script>
var canWidth = 400;
var canHeight = 100;
//position where the frame will be drawn
var x = 0;
var y = 0;
var srcX;
var srcY;
var sheetWidth = 230;
var sheetHeight = 79;
var frameCount = 5;
var width = sheetWidth/frameCount;
var height;
var currentFrame = 0;
var tree = new Image();
tree.src = "tree sprite.jpg"
var canvas = document.getElementById('canvas');
canvas.width = canWidth;
canvas.height = canHeight;
var ctx = canvas.getContext('2d');
function updateFrame(){
currentFrame = ++currentFrame%frameCount
srcX = currentFrame*width;
srcY = 0;
ctx.clearRect(x, y, width, height);
}
function draw(){
updateFrame();
}
setInterval(function(){
draw();
}, 100);
</script>
</body>
</html>
I expect the output to be an animation of a tree growing, but instead I'm getting a blank page.
you should provide more code or a snippet, there are several variables didn't show up in your code
and it's hard to debug your code if u use setInterval, you should make sure your code can work first.
maybe you can try step by step:
draw the whole img on the canvas first. if it works, go next
invoke your draw() function manually, check if the img drawed
invoke more, such assetTimout(draw, 1000), check the result
ok, and i think you can console.log these variables in draw
I am beginning to learn javascript and i want to add a random color to each random line that gets generated in this project...
var c = document.createElement('canvas');
document.body.appendChild(c);
var ctx = c.getContext('2d');
c.width = window.innerWidth;
c.height = window.innerHeight;
var position = 0;
ctx.lineWidth = window.prompt("what line width do you want?","0.5");
ctx.color = "#"+((1<<24)*Math.random()|0).toString(16);
function animateCircle(position) {
ctx.clearRect(0,0,c.width,c.height);
ctx.arc(c.width/2,c.height/2,(c.width > c.height ? c.height : c.width)/3,position,position+Math.random()*10,false);ctx.stroke();
}
window.setInterval(function() {
animateCircle(position);
position += 3;
}, 10);
I wanted it to make it so that every generated line was a different random color every time so i tried using the ctx.color but it doesn't seem to apply to the generated lines but instead just stay default color black. It looks like it is skipping over it totally. And when I print it the script doesn't even seem to start...
but my ctx.color doesn't work and i don't understand why..
Please help
thanks.
What you're looking for is strokeStyle, not color. Try:
function animateCircle(position) {
ctx.strokeStyle = "#"+((1<<24)*Math.random()|0).toString(16);
ctx.arc(c.width/2,c.height/2,(c.width > c.height ? c.height : c.width)/3,position,position+Math.random()*10,false);
ctx.stroke();
}
You will have an unexpected result (that I let you discover), but it should help you understand a bit more about canvas :)
Use strokeStyle instead of color. There are a couple of other issues you will notice as well which I point out below:
function animateCircle(position) {
ctx.strokeStyle = "#"+((1<<24)*Math.random()|0).toString(16);
ctx.clearRect(0,0,c.width,c.height);
/// remember beginPath() here or the arc will accumulate
ctx.beginPath();
ctx.arc(c.width/2,c.height/2,(c.width > c.height ? c.height : c.width)/3,position,position+Math.random()*10,false);
ctx.stroke();
}
To draw a line, use this instead of the arc():
ctx.moveTo(x1, y1); /// replace x and y with your positions
ctx.lineTo(x2, y2);
ctx.stroke();
Increase the interval to at least 16ms
window.setInterval(function() {
animateCircle(position);
position += 3;
}, 16);
or preferably use requestAnimtionFrame for smoother animations:
(function loop() {
animateCircle(position);
position += 3;
requestAnimtionFrame(loop);
})();
I've tried everything to solve this problem and nothings working. Posting here is my last resort.
I'm trying to code a simple canvas element and load everything dynamically and nothing is working. I keep getting the error in Google console "Uncaught ReferenceError: ctx is not defined" on game.js:33.
I originally thought it was because common.js is being loaded after the other 2 javascript files so I made a loader file which loads each JS file in the order I want. I did this using $.getScript() and the $.when() function . Unfortunately, this did not work. So the ctx var is being loaded but it's giving me the error for some reason.
I've included the files below. Thanks in advance for any help.
EDIT: I just tried taking all the code out of each individual JS file and putting them in the same one in the order they are meant to be in and it works fine now. But it would be nice to know why it was not working at all. As it's going to get very hectic having all my code in 1 file. Thank you.
index.php
<!doctype>
<html>
<head>
<title>Game - Unknown</title>
<link rel="stylesheet" href="./assets/css/default.css">
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.3.min.js"></script>
<!--<script src="./assets/js/loader.js"></script>-->
<script src="./assets/js/common.js"></script>
<script src="./assets/js/graphics.js"></script>
<script src="./assets/js/game.js"></script>
</head>
<body>
<canvas id="viewport"></canvas>
</body>
</html>
common.js
$(document).ready(function(){
// Setup the canvas game context for 2D
var canvas = document.getElementById("viewport");
var ctx = canvas.getContext("2d");
// Update the canvas dimensions to match window size when changed
$(window).resize(function(){canvasResize()}); canvasResize();
function canvasResize(){
var cWidth = window.innerWidth;
var cHeight = window.innerHeight;
canvas.width = cWidth;
canvas.height = cHeight;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, cWidth, cHeight);
}
// Get center of things - Useful for centering images on canvas
function getCenter(dim){ return dim / 2 }
});
graphics.js
$(document).ready(function(){
function gfxTile(x, y, w, h, r, g, b, a)
{
ctx.fillStyle = "rgb(60, 60, 100)";
ctx.beginPath();
//ctx.moveTo(x + h / 2, y + w / 2);
ctx.moveTo(10, 10);
ctx.lineTo(105, 25);
ctx.lineTo(25, 105);
ctx.lineTo(25, 105);
ctx.closePath();
ctx.fill();
}
});
game.js
$(document).ready(function(){
// START TEMPORARY
var mapSizeX = 10;
var mapSizeY = 10;
var mapArray = new Array();
function createMap()
{
for(x = 0; x < mapSizeX; x++)
{
mapArray[x] = [];
for(y = 0; y < mapSizeY; y++)
{
mapArray[x][y] = 0;
}
}
}
// END TEMPORARY
setInterval(mainLoop, 50);
function mainLoop()
{
//gfxTile(10, 10, 40, 40, 50, 50, 50, 100);
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
});
var ctx = canvas.getContext("2d"); created a variable called ctx in the scope of the function you passed into $(document).ready();
When the function inside of game.js executes, there is no ctx variable in its scope, or the parent scope, window.
An easy fix would be changing
var ctx = canvas.getContext("2d");
to
window.ctx = canvas.getContext("2d");
Your ctx variable is already on a reachable namespace, your problem is basically a result of loading files with no order and priority. You can however use a script loader to solve the problem and make sure your variable is already defined before using it.
In this example I'm using the head.js script loader.
<script src="js/head.js"></script>
<script>
head.js(
"http://code.jquery.com/jquery-1.9.1.min.js",
"http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.3.min.js",
"js/common.js",
"js/graphics.js",
"js/game.js"
);
You can optimize your code even more using requireJS to expose only the namespaces you want . Check them out.