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>
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 a problem with canvas.
i can draw a canvas with single image, but i can't draw each canvas with image separate.
- if data just have one image it's working fine, but data have multiple image it's not working
can you help me ?
<script>
var h_notepad = 500;
var w_notepad = 737;
var data = [
{dataImageURL: "1_sat_1.png"},
{dataImageURL: "1_sat_2.png"},
{dataImageURL: "1_sat_3.png"},
{dataImageURL: "1_sat_4.png"}
];
for(var i = 0; i < data.length ; i++){
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
canvas.width = w_notepad;
canvas.height = h_notepad;
img.crossOrigin = 'anonymous';
img.width = w_notepad;
img.height = h_notepad;
console.log(img);
img.onload = function(){
ctx.drawImage(img, 0, 0, w_notepad, h_notepad);
};
img.src = data[i].dataImageURL;
$('body').append(canvas);
}
</script>
<html>
<head>
<meta charset="utf-8">
<title>DRAWING</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<meta charset="utf-8">
</head>
<body>
</body>
</html>
I guess you only get the last one.
It's a closure problem.
When the load event fires, img and ctx only refer to the last ones created.
So you draw data.length time on the same canvas.
To avoid it, you can use this and wrap the canvas creation in the onload handler:
var imgs = ['http://lorempixel.com/200/300/', 'http://lorempixel.com/500/300/', 'http://lorempixel.com/200/100/'];
for (var i = 0; i < imgs.length; i++) {
var img = new Image();
var width = 500;
var height = 300;
img.onload = function() {
var c = document.createElement('canvas');
c.width = width;
c.height = height;
document.body.appendChild(c);
var ctx = c.getContext('2d');
ctx.drawImage(this, 0,0, width, height);
};
img.src = imgs[i];
}
Problem is that onload is asynchronous. So all your code runs before any of onload functions will be called. That is why your function
img.onload = function(){
ctx.drawImage(img, 0, 0, w_notepad, h_notepad);
};
uses the latest ctx and renders all images in this context.
What you can do is cover this asynchronous call with a synchronous function scope:
(function(ctx, img, w_notepad, h_notepad) {
img.onload = function(){
ctx.drawImage(img, 0, 0, w_notepad, h_notepad);
};
})(ctx, img, w_notepad, h_notepad);
This isolates the variables and keeps there values until you receive the image.
When I draw a line, it is drawn and appears fine, but when I draw a new line again, the last thing I drew disappears. It's like resetting after I draw a new line again.
Here's my code:
function draw() {
var teethImage = new Image();
teethImage.onload = function () {
};
teethImage.src = "images/teeth.png";
ctx.drawImage(teethImage, 1, 1);
ctx.lineTo(currX, currY);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.stroke();
}
What's wrong in my code?
I already fixed the error. I just simply load the image on the init().
Because the reason why it is resetting, it displays again a new image, thus, it overwrites the image.
here try this:
draw();
function draw() {
var canvas = document.getElementById('myCanvas'),
ctx = canvas.getContext('2d');
var img = new Image();
img.onload = function () {
ctx.drawImage(img, 1, 1);
ctx.moveTo(0,0); // startX,startY (i mean 'start point which you want:x, start point which you want:y')
ctx.lineTo(img.width, img.height);
ctx.lineTo(img.width/3, img.height/2);
ctx.lineTo(img.width, 0);
ctx.strokeStyle = '#fff'; //you used (strokeStyle(x), what is x)
ctx.lineWidth = 7;
ctx.stroke();
};
img.src = "http://bermangraphics.com/images/jury-images-13.jpg";
}
You can examine strokeStyle
JsFiddle Demo
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.
Say we have a canvas:
<canvas id="one" width="100" height="200"></canvas>
And on a button click the canvas gets rotated 90 degrees clockwise (around the center) and the dimensions of the canvas get also updated, so in a sense it looks like this afterwards:
<canvas id="one" width="200" height="100"></canvas>
Note that the id of the canvas is the same.
Imagine simply rotating an image clockwise without it being cropped or being padded.
Any suggestions before I do it the long way of creating a new canvas and rotating and copying pixel by pixel?
UPDATE sample code with suggestion from comments still not working:
function imageRotatecw90(){
var canvas = document.getElementById("one");
var context = canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var myImageData = context.getImageData(0,0, cw,ch);
context.save();
context.translate(cw / 2, ch / 2);
context.rotate(Math.PI/2);
context.putImageData(myImageData, 0, 0);
context.restore();
canvas.width=ch;
canvas.height=cw;
}
FiddleJS
Look at this DEMO.
To achieve the results seen in demo, I made use of canvas.toDataURL to cache the canvas into an image, then reset the canvas to their new dimensions, translate and rotate the context properly and finally draw the cached image back to modified canvas.
That way you easily rotate the canvas without need to redraw everything again. But because anti-aliasing methods used by browser, each time this operation is done you'll notice some blurriness in result. If you don't like this behavior the only solution I could figure out is to draw everything again, what is much more difficult to track.
Here follows the code:
var canvas = document.getElementById("one");
var context = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
// Sample graphic
context.beginPath();
context.rect(10, 10, 20, 50);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
// create button
var button = document.getElementById("rotate");
button.onclick = function () {
// rotate the canvas 90 degrees each time the button is pressed
rotate();
}
var myImageData, rotating = false;
var rotate = function () {
if (!rotating) {
rotating = true;
// store current data to an image
myImageData = new Image();
myImageData.src = canvas.toDataURL();
myImageData.onload = function () {
// reset the canvas with new dimensions
canvas.width = ch;
canvas.height = cw;
cw = canvas.width;
ch = canvas.height;
context.save();
// translate and rotate
context.translate(cw, ch / cw);
context.rotate(Math.PI / 2);
// draw the previows image, now rotated
context.drawImage(myImageData, 0, 0);
context.restore();
// clear the temporary image
myImageData = null;
rotating = false;
}
}
}
Rotation
Note it is not possible to rotate a single element.
ctx.save();
ctx.rotate(0.17);
// Clear the current drawings.
ctx.fillRect()
// draw your object
ctx.restore();
Width/height adjustment
The only way I ever found to properly deal with display ratios, screen sizes etc:
canvas.width = 20;// DO NOT USE PIXELS
canvas.height = 40; // AGAIN NO PIXELS
Notice I am intentionally not using canvas.style.width or canvas.style.height. Also for an adjustable canvas don't rely on CSS or media queries to do the transformations, they are a headache because of the pixel ratio differences. JavaScript automatically accounts for those.
Update
You also have to update the width and the height before you draw. Not sure what you are trying to achieve, but I guess this isn't a problem:
Demo here
var canvas = document.getElementById("one");
var context = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
canvas.width = 200;
canvas.height = 400;
// Sample graphic
context.beginPath();
context.rect(10,10,20,50);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
var myImageData = context.getImageData(0, 0, cw, ch);
context.save();
context.translate(cw / 2, ch / 2);
context.putImageData(myImageData, 0, 0);
context.rotate(0.20);
If you want to rotate an image by 90 degrees this might be helpful:
export const rotateBase64Image = async (base64data: string) => {
const image = new Image();
image.src = base64data;
return new Promise<string>((resolve, reject) => {
image.onload = function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("cannnot get context '2d'");
canvas.width = image.height;
canvas.height = image.width;
ctx.setTransform(0, 1, -1, 0, canvas.width, 0); // overwrite existing transform
ctx!.drawImage(image, 0, 0);
canvas.toBlob((blob) => {
if (!blob) {
return reject("Canvas is empty");
}
const fileUrl = window.URL.createObjectURL(blob);
resolve(fileUrl);
}, "image/jpeg");
};
});
};
If you don't have image in base64 format you can do it like this:
const handleRotate = async () => {
const res = await fetch(link);
const blob = await res.blob();
const b64: string = await blobToB64(blob);
const rotatedImage = await rotateBase64Image(b64)
setLink(rotatedImage);
}
Here is my blobTob64 function:
export const blobToB64 = async (blob) => {
return new Promise((resolve, _) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
};