I made the following script. When I push the button, the word "blood" moves, and push the button and it stops.
This script worked in Chrome but when I move the following scripts to the TOP line. (between var flag; and window.setInterval), an error happens saying
uncaught typeerror cannot call method 'getcontext' of null"
would you explain why it happens please?
var canvas = document.getElementById("canvas"), ctx = canvas.getContext("2d");
var y = 100;
var flag = false;
window.onload = setInterval(function(){
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "red";
ctx.font = "50px Helvetica";
ctx.fillText("blood", 200, y);
if(flag){
y++;
}
}, 30);
function start(){
flag = true;
}
function stop(){
flag = false;
}
If you run your code in a script element at the end of the html body, you can ensure that it doesn't try to get the canvas context before the DOM is ready. Also, I made an optimization to your code to cache the DOM lookup:
<html>
<head>
<title>Canvas Scrolling Text</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="text/javascript">
var y = 100;
var flag = false;
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
setInterval(function() {
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "red";
ctx.font = "50px Helvetica";
ctx.fillText("blood", 100, y);
if(flag)
y++;
}, 30);
function start()
{
flag = true;
}
function stop()
{
flag = false;
}
start();
</script>
</body>
Here's a working jsFiddle.
If you move it out of the onload function, then the DOM elements (specifically your canvas) don't exist yet. See https://stackoverflow.com/a/15564394/1370556 for more information on DOM content loaded events.
Related
This question already has an answer here:
How to render icon fonts on HTML canvas and Material Design icon fonts in particular?
(1 answer)
Closed 2 years ago.
I saw a code pen here for rendering Microsoft Fabric UI Icons in a canvas in code pen. This is the working link
https://codepen.io/joshmcrty/pen/GOBWeV
There is a lot of extra stuff here to customize it. So I attempted to minimize this to barebones. What I did is completely remove the css, change the HTML to just the canvas tag, and reduced the JS code. The result looks like this:
HTML
<canvas id="canvas" width="92" height="92"></canvas>
CSS
/*none*/
JS
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
function getFontIconCharacter(className, pseudo = ':before') {
var testI = document.createElement('i');
var char;
testI.className = `ms-Icon ms-Icon--${className}`;
document.body.appendChild(testI);
char = window.getComputedStyle(testI, pseudo).getPropertyValue('content').replace(/'|"/g, '');
testI.remove();
return char;
}
function drawRect() {
context.fillStyle = "#777777";
context.fillRect(0, 0, canvas.width, canvas.height);
}
function drawIcon() {
context.clearRect(0, 0, canvas.width, canvas.height);
drawRect();
context.fillStyle = "#FFFFFF";
let fontClass = "Contact",
fontSize = 56,
topOffset = 46,
leftOffset = 46;
context.font = fontSize + "px FabricMDL2Icons";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText(getFontIconCharacter(fontClass), parseInt(leftOffset, 10), parseInt(topOffset, 10));
}
window.addEventListener('load', function() {
drawIcon();
});
The weird thing is if I replace the code from the working example into the above, the custom font in the canvas still renders correctly. However if I recreate it on a new file, it does not render the font properly. I saved my attempt here
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Fabric UI Icons</title>
<link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/11.0.0/css/fabric.min.css">
</head>
<body>
<canvas id="canvas" width="92" height="92"></canvas>
<script>
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
function getFontIconCharacter(className, pseudo = ':before') {
var testI = document.createElement('i');
var char;
testI.className = `ms-Icon ms-Icon--${className}`;
document.body.appendChild(testI);
char = window.getComputedStyle(testI, pseudo).getPropertyValue('content').replace(/'|"/g, '');
testI.remove();
return char;
}
function drawCircle() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = canvas.width / 2;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = "#777777";
context.fill();
}
function drawRect() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.fillStyle = "#777777";
context.fillRect(0, 0, canvas.width, canvas.height);
}
function drawIcon() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height);
drawRect();
context.fillStyle = "#ffffff"; // icon color
let fontClass = "Contact",
fontSize = 56,
topOffset = 46,
leftOffset = 46;
context.font = fontSize + "px FabricMDL2Icons";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText(getFontIconCharacter(fontClass), parseInt(leftOffset, 10), parseInt(topOffset, 10));
}
window.addEventListener('load', function() {
drawIcon();
});
</script>
</body>
</html>
I noticed they include a css file to the font, so I have that as well, but it still doesn't work.
Does anyone see what is wrong?
Thanks
I figured it out, I had to start the font loading process with this. Just because it's defined in a <link> tag doesn't mean it will start loading. Something needs to reference it before its used on the canvas for the browser to load it.
<style>
body {
font-family: FabricMDL2Icons;
}
</style>
I Have build some code related to canvas but code is working on TRYIT but code is not working locally when i have copied all code to file and tried to run it .
This is what this code is doing , it takes an image and set the width and height of canvas with respect to that image and draw a filled circle with text in it on that image(canvas).
Here is code
<head>
<meta charset=utf-8 />
<title>Draw a circle</title>
</head>
<body onload="draw();">
<canvas id="circle"></canvas>
</body>
<script>
var canvas = document.getElementById('circle'),
context = canvas.getContext('2d');
function draw()
{
base_image = new Image();
base_image.src = 'http://sst-system.com/old/Planos/C21E34.JPG';
var canvas = document.getElementById('circle');
if (canvas.getContext)
{
base_image = new Image();
base_image.src = 'http://sst-system.com/old/Planos/C21E34.JPG';
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.canvas.width = base_image.width;
ctx.canvas.height = base_image.height;
var X = 500;
var Y = 229;
var R = 6.4;
ctx.font = "15px Arial bold";
ctx.beginPath();
ctx.arc(X, Y, R, 0, 2 * Math.PI, false);
ctx.lineWidth = 12;
ctx.strokeStyle = '#FF0000';
ctx.drawImage(base_image, 0, 0)
ctx.stroke();
ctx.fillText("TT", X-9, Y+5);
}
}
</script>
There are no errors on console , but it shows these warnings in console :
As #DBS mentioned, it was a speed thing. You weren't waiting for the image to load before working with it.
The fix is to attach a listener to the load event of the image, either using image.addEventListener('load', () => { or the deprecated-but-still-works style of image.onload = () => {, which I have used below.
The reason that it works in the TryIt example is that the image is cached by the browser from the second load onwards, so it is available immediately and you don't need to wait for it to load.
I suspect that when you run it locally, if you have Devtools open, the cache is disabled due to an option in Devtools settings called "Disable cache (while DevTools is open)". So it will never be pulled from the cache, and thus never work.
The following code works:
<html>
<head>
<meta charset=utf-8 />
<title>Draw a circle</title>
</head>
<body onload="draw();">
<canvas id="circle"></canvas>
</body>
<script>
var canvas = document.getElementById('circle'),
context = canvas.getContext('2d');
function draw() {
base_image = new Image();
base_image.src = 'http://sst-system.com/old/Planos/C21E34.JPG';
var canvas = document.getElementById('circle');
if (canvas.getContext) {
base_image = new Image();
base_image.src = 'http://sst-system.com/old/Planos/C21E34.JPG';
// The key change: put the rest of the code inside the onload callback
// to wait for the image to load before using it.
base_image.onload = function() {
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.canvas.width = base_image.width;
ctx.canvas.height = base_image.height;
var X = 500;
var Y = 229;
var R = 6.4;
ctx.font = "15px Arial bold";
ctx.beginPath();
ctx.arc(X, Y, R, 0, 2 * Math.PI, false);
ctx.lineWidth = 12;
ctx.strokeStyle = '#FF0000';
ctx.drawImage(base_image, 0, 0)
ctx.stroke();
ctx.fillText("TT", X - 9, Y + 5);
};
}
}
</script>
</html>
I want to create something like scratch card.
I created a canvas and added text to it.I than added a box over the text to hide it.Finally write down the code to erase(scratch) that box.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "30px Arial";
ctx.fillText("Hello World",10,50);
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle='red';
ctx.fillRect(0,0,500,500);
function myFunction(event) {
var x = event.touches[0].clientX;
var y = event.touches[0].clientY;
document.getElementById("demo").innerHTML = x + ", " + y;
ctx.globalCompositeOperation = 'destination-out';
ctx.arc(x,y,30,0,2*Math.PI);
ctx.fill();
}
But the problem is it delete the text also.
How could I only delete that box not the text?
Canvas context keeps only one drawing state, which is the one rendered. If you modify a pixel, it won't remember how it was before, and since it has no built-in concept of layers, when you clear a pixel, it's just a transparent pixel.
So to achieve what you want, the easiest is to build this layering logic yourself, e.g by creating two "off-screen" canvases, as in "not appended in the DOM", one for the scratchable area, and one for the background that should be revealed.
Then on a third canvas, you'll draw both canvases every time. It is this third canvas that will be presented to your user:
var canvas = document.getElementById("myCanvas");
// the context that will be presented to the user
var main = canvas.getContext("2d");
// an offscreen one that will hold the background
var background = canvas.cloneNode().getContext("2d");
// and the one we will scratch
var scratch = canvas.cloneNode().getContext("2d");
generateBackground();
generateScratch();
drawAll();
// the events handlers
var down = false;
canvas.onmousemove = handlemousemove;
canvas.onmousedown = handlemousedown;
canvas.onmouseup = handlemouseup;
function drawAll() {
main.clearRect(0,0,canvas.width,canvas.height);
main.drawImage(background.canvas, 0,0);
main.drawImage(scratch.canvas, 0,0);
}
function generateBackground(){
background.font = "30px Arial";
background.fillText("Hello World",10,50);
}
function generateScratch() {
scratch.fillStyle='red';
scratch.fillRect(0,0,500,500);
scratch.globalCompositeOperation = 'destination-out';
}
function handlemousedown(evt) {
down = true;
handlemousemove(evt);
}
function handlemouseup(evt) {
down = false;
}
function handlemousemove(evt) {
if(!down) return;
var x = evt.clientX - canvas.offsetLeft;
var y = evt.clientY - canvas.offsetTop;
scratch.beginPath();
scratch.arc(x, y, 30, 0, 2*Math.PI);
scratch.fill();
drawAll();
}
<canvas id="myCanvas"></canvas>
Now, it could all have been done on the same canvas, but performance wise, it's probably not the best, since it implies generating an overly complex sub-path that should get re-rendered at every draw, also, it is not much easier to implement:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext('2d');
ctx.font = '30px Arial';
drawAll();
// the events handlers
var down = false;
canvas.onmousemove = handlemousemove;
canvas.onmousedown = handlemousedown;
canvas.onmouseup = handlemouseup;
function drawAll() {
ctx.globalCompositeOperation = 'source-over';
// first draw the scratch pad, intact
ctx.fillStyle = 'red';
ctx.fillRect(0,0,500,500);
// then erase with the currently being defined path
// see 'handlemousemove's note
ctx.globalCompositeOperation = 'destination-out';
ctx.fill();
// finally draw the text behind
ctx.globalCompositeOperation = 'destination-over';
ctx.fillStyle = 'black';
ctx.fillText("Hello World",10,50);
}
function handlemousedown(evt) {
down = true;
handlemousemove(evt);
}
function handlemouseup(evt) {
down = false;
}
function handlemousemove(evt) {
if(!down) return;
var x = evt.clientX - canvas.offsetLeft;
var y = evt.clientY - canvas.offsetTop;
// note how here we don't create a new Path,
// meaning that all the arcs are being added to the single one being rendered
ctx.moveTo(x, y);
ctx.arc(x, y, 30, 0, 2*Math.PI);
drawAll();
}
<canvas id="myCanvas"></canvas>
How could I only delete that box not the text?
You can't, you'll have to redraw the text. Once you've drawn the box over the text, you've obliterated it, it doesn't exist anymore. Canvas is pixel-based, not shape-based like SVG.
I am trying to do a simple animation using window.requestAnimationFrame().Somehow,The function is not getting called recursively and not giving the correct code.The javascript code of my file is:
function OilPainting(){
this.initialize=function(){
var x=100;
var canvas=document.getElementById("canvas");
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
var context=canvas.getContext('2d');
animate(canvas,context);
}
}
var x=100;
var animate=function(canvas,context){
window.requestAnimationFrame(animate);
console.log("a");
//console.log("a");
/*for(var i=0;i<1;i++){
var x=Math.random()*window.innerWidth;
var y=Math.random()*window.innerHeight;
context.beginPath();
context.arc(x,y,30,0,2*Math.PI);
context.stroke();
//console.log("here");
x+=1;*/
context.beginPath();
context.arc(x,100,30,0,2*Math.PI);
context.clearRect(0,0,innerWidth,innerHeight);
//console.log(x);
context.stroke();
x+=100;
// console.log(x);
}
var app=new OilPainting();
app.initialize();
Here, although the console a is getting printed recursively but the circles are not getting formed.The link to my Codepen is Here.How exactly is requestAnimationFrame() is used?
You have too many issues with your code ...
Here's how it should be done
var x = 100;
function OilPainting() {
this.initialize = function() {
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var context = canvas.getContext('2d');
animate(canvas, context);
}
}
var animate = function(canvas, context) {
//console.log("a");
context.clearRect(0, 0, innerWidth, innerHeight);
context.beginPath();
context.arc(x, 100, 30, 0, 2 * Math.PI);
context.stroke();
x += 1;
requestAnimationFrame(function() {
animate(canvas, context);
});
}
var app = new OilPainting();
app.initialize();
body{margin:0;overflow:hidden}canvas{border:1px solid #d3d3d3}
<canvas id="canvas"></canvas>
apology for not giving any explanation
you didn't pass canvas and context to animate function when calling requestAnimationFrame
var animate=function(canvas,context){
window.requestAnimationFrame(function() {
animate(canvas, context)
});
console.log("a");
context.beginPath();
context.arc(x,100,30,0,2*Math.PI);
//console.log(x);
context.stroke();
x+=100;
}
i used a udemy course to make a simple pong game.
I am now recreating it again as practice and i keep getting the error canvas is null. Any Help?
<html>
<canvas id="gamecanvas" width="600" height="800"></canvas>
<script>
var canvas = null;
var Context = null;
window.onload = function(){
canvas.document.getElementById("gamecanvas");
Context.canvas.getContext("2d");
drawEverything();
}
function drawEverything() {
Context.fillStyle = "black";
Context.fillRect(0,0,canvas.width,canvas.height);
Context.fillStyle = "white";
Context.fillRect(0,360,20,80);
Context.fillStyle = "white";
Context.fillRect(580,360,20,80);
Context.fillStyle = "green";
context.beginpath();
context.arc(0,0,10,10,Math.PI*2,true);
context.fill();
}
That is because you set it to null! Change
canvas.document.getElementById("gamecanvas");
To
canvas = document.getElementById("gamecanvas");
Otherwise, you never set canvas.
You should do like this.
canvas = document.getElementById("gamecanvas");
Context = canvas.getContext("2d");
You set canvas to null so of course it will be null. This should help.
canvas = document.getElementById("gamecanvas")
context = canvas.getContext("2d");