I cannot get my image to display in an HTML canvas using drawImage(). I know the filepath is correct because the image displays just fine when I disable display: none;
I know this can occur when you attempt to draw an image before it is loaded, which you can avoid by using the onload property.
Here is the relevant HTML.
<canvas id='gameScreen'></canvas>
<img id="imgBall"src="assets/images/ball2.png" alt="ball">
<script type="module"src="src/index.js"></script>
Here is the relevant Javascript.
// CANVAS SETUP
let canvas = document.getElementById("gameScreen");
canvas.width = 800;
canvas.height = 600;
let ctx = canvas.getContext("2d");
// DISPLAY BALL
let imgBall = document.getElementById('imgBall');
imgBall.onload = function() {
console.log(imgBall.src + ' successfully loaded');
ctx.drawImage(imgBall, 10, 10);
}
imgBall.onerror = function() {
console.log(imgBall.src + ' failed to load');
}
What makes this so confusing to me is that I'm not getting any console messages from imgBall.onload, or from imgBall.onerror.
I'm pretty new to Javascript and canvas, so I apologize if the answer is extremely obvious.
To fix the issue I am dynamically creating an image tag and then giving it a src AFTER I have added the event handlers for error and onload.
This makes me think that the issue is due to the image loading BEFORE your script tag had a chance to add the event listeners to the img tag.
// CANVAS SETUP
let canvas = document.getElementById("gameScreen");
canvas.width = 800;
canvas.height = 600;
let ctx = canvas.getContext("2d");
// DISPLAY BALL
const imgBall = document.createElement("img");
imgBall.onload = function() {
console.log(imgBall.src + ' successfully loaded');
ctx.drawImage(fitImageOntoCanvas(imgBall,25,25), 10, 10);
}
imgBall.onerror = function() {
console.log(imgBall.src + ' failed to load');
}
imgBall.src = "https://pngimg.com/uploads/football/football_PNG52737.png"
// need to resize because my ball image is huge
// thanks #markE https://stackoverflow.com/questions/38664890/resize-images-with-canvas-before-uploading
function fitImageOntoCanvas(img,MAX_WIDTH,MAX_HEIGHT){
// calculate the scaling factor to resize new image to
// fit MAX dimensions without overflow
var scalingFactor=Math.min((MAX_WIDTH/img.width),(MAX_HEIGHT/img.height))
// calc the resized img dimensions
var iw=img.width*scalingFactor;
var ih=img.height*scalingFactor;
// create a new canvas
var c=document.createElement('canvas');
var ctx=c.getContext('2d');
// resize the canvas to the new dimensions
c.width=iw;
c.height=ih;
// scale & draw the image onto the canvas
ctx.drawImage(img,0,0,iw,ih);
// return the new canvas with the resized image
return(c);
}
<canvas id="gameScreen" height="500" width="500"></canvas>
Is there a default way of drawing an SVG file onto a HTML5 canvas? Google Chrome supports loading the SVG as an image (and simply using drawImage), but the developer console does warn that resource interpreted as image but transferred with MIME type image/svg+xml.
I know that a possibility would be to convert the SVG to canvas commands (like in this question), but I'm hoping that's not needed. I don't care about older browsers (so if FireFox 4 and IE 9 will support something, that's good enough).
EDIT: Dec 2019
The Path2D() constructor is supported by all major browsers now, "allowing path objects to be declared on 2D canvas surfaces".
EDIT: Nov 2014
You can now use ctx.drawImage to draw HTMLImageElements that have a .svg source in some but not all browsers (75% coverage: Chrome, IE11, and Safari work, Firefox works with some bugs, but nightly has fixed them).
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
}
img.src = "http://upload.wikimedia.org/wikipedia/commons/d/d2/Svg_example_square.svg";
Live example here. You should see a green square in the canvas. The second green square on the page is the same <svg> element inserted into the DOM for reference.
You can also use the new Path2D objects to draw SVG (string) paths. In other words, you can write:
var path = new Path2D('M 100,100 h 50 v 50 h 50');
ctx.stroke(path);
Live example of that here.
Original 2010 answer:
There's nothing native that allows you to natively use SVG paths in canvas. You must convert yourself or use a library to do it for you.
I'd suggest looking in to canvg: (check homepage & demos)
canvg takes the URL to an SVG file, or the text of the SVG file, parses it in JavaScript and renders the result on Canvas.
Further to #Matyas answer: if the svg's image is also in base64, it will be drawn to the output.
Demo:
var svg = document.querySelector('svg');
var img = document.querySelector('img');
var canvas = document.querySelector('canvas');
// get svg data
var xml = new XMLSerializer().serializeToString(svg);
// make it base64
var svg64 = btoa(xml);
var b64Start = 'data:image/svg+xml;base64,';
// prepend a "header"
var image64 = b64Start + svg64;
// set it as the source of the img element
img.onload = function() {
// draw the image onto the canvas
canvas.getContext('2d').drawImage(img, 0, 0);
}
img.src = image64;
svg, img, canvas {
display: block;
}
SVG
<svg height="40" width="40">
<rect width="40" height="40" style="fill:rgb(255,0,255);" />
<image xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAEX0lEQVQ4jUWUyW6cVRCFv7r3/kO3u912nNgZgESAAgGBCJgFgxhW7FkgxAbxMLwBEmIRITbsQAgxCEUiSIBAYIY4g1EmYjuDp457+Lv7n+4tFjbwAHVOnVPnlLz75ht67OhhZg/M0p6d5tD9C8SNBBs5XBJhI4uNLC4SREA0UI9yJr2c4e6QO+v3WF27w+rmNrv9Pm7hxDyHFg5yYGEOYxytuRY2SYiSCIwgRgBQIxgjEAKuZWg6R9S0SCS4qKLZElY3HC5tp7QPtmlMN7HOETUTXBJjrEGsAfgPFECsQbBIbDGJZUYgGE8ugQyPm+o0STtTuGZMnKZEjRjjLIgAirEOEQEBDQFBEFFEBWLFtVJmpENRl6hUuFanTRAlbTeZarcx0R6YNZagAdD/t5N9+QgCYAw2jrAhpjM3zaSY4OJGTDrVwEYOYw2qioigoviq5MqF31m9fg1V5fCx+zn11CLNVnufRhBrsVFE1Ihpthu4KDYYwz5YQIxFBG7duMZnH31IqHL6wwnGCLFd4pez3/DaG2/x4GNPgBhEZG/GGlxkMVFkiNMYay3Inqxed4eP33uf7Y0uu90xWkGolFAru7sZn5w5w921m3u+su8vinEO02hEWLN/ANnL2rkvv2an2yd4SCKLM0JVBsCgAYZZzrnPP0eDRzXgfaCuPHXwuEYjRgmIBlQVVLl8/hKI4fRzz3L6uWe5+PMvnHz6aa4uX+D4yYe5vXaLH86eoyoLjLF476l9oKo9pi5HWONRX8E+YznOef7Vl1h86QWurlwjbc+QpikPPfoIcZLS39pmMikp8pzae6q6oqgriqrGqS+xeLScoMYSVJlfOMTl5RXW1+5w5fJVnFGWf1/mxEMnWPppiclkTLM5RdJoUBYFZVlQ5DnZMMMV167gixKLoXXsKGqnOHnqOJ/+/CfZ+XUiZ0jTmFv5mAvf/YjEliQ2vPD8Ir6qqEcZkzt38cMRo5WruFvfL9FqpyRxQhj0qLOax5I2S08+Tu/lFiGUGOPormxwuyfMnjrGrJa88uIixeYWl776lmrzNjmw8vcG8sU7ixpHMXFsCUVg9tABjEvRgzP82j7AhbyiX5Qcv2+Bvy7dYGZ1k7efeQB/Y4PBqGBtdYvb3SFzLcfqToZc/OB1zYeBSpUwLBlvjZidmWaSB1yaYOfn6LqI/r0hyU6P+cRSlhXjbEI2zvnt7y79oqQ3qeg4g6vKjCIXehtDmi6m0UnxVnCRkPUHVNt9qkLJxgXOCYNOg34v48raPaamU2o89/KKsQ9sTSpc0JK7NwdcX8s43Ek5cnSOLC/Z2R6Rj0ra0w2W1/t0xyWn51uk2Ri1QtSO6OU5d7OSi72cQeWxKG7p/Dp//JXTy6C1Pcbc6DMpPRtjTxChEznWhwVZUCKrjCrPoPDczHLmnLBdBgZlRRWUEBR3ZKrme5TlrTGlV440Y1IrXM9qQGi6mkG5V6uza7tUIeCDElTZ1L26elX+fcH/ACJBPYTJ4X8tAAAAAElFTkSuQmCC" height="20px" width="20px" x="10" y="10"></image></svg><br/>
IMAGE
<img/><br/>
CANVAS
<canvas></canvas><br/>
You can easily draw simple svgs onto a canvas by:
Assigning the source of the svg to an image in base64 format
Drawing the image onto a canvas
Note: The only drawback of the method is that it cannot draw images embedded in the svg. (see demo)
Demonstration:
(Note that the embedded image is only visible in the svg)
var svg = document.querySelector('svg');
var img = document.querySelector('img');
var canvas = document.querySelector('canvas');
// get svg data
var xml = new XMLSerializer().serializeToString(svg);
// make it base64
var svg64 = btoa(xml);
var b64Start = 'data:image/svg+xml;base64,';
// prepend a "header"
var image64 = b64Start + svg64;
// set it as the source of the img element
img.src = image64;
// draw the image onto the canvas
canvas.getContext('2d').drawImage(img, 0, 0);
svg, img, canvas {
display: block;
}
SVG
<svg height="40">
<rect width="40" height="40" style="fill:rgb(255,0,255);" />
<image xlink:href="https://en.gravatar.com/userimage/16084558/1a38852cf33713b48da096c8dc72c338.png?size=20" height="20px" width="20px" x="10" y="10"></image>
</svg>
<hr/><br/>
IMAGE
<img/>
<hr/><br/>
CANVAS
<canvas></canvas>
<hr/><br/>
Mozilla has a simple way for drawing SVG on canvas called "Drawing DOM objects into a canvas"
As Simon says above, using drawImage shouldn't work. But, using the canvg library and:
var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
ctx.drawSvg(SVG_XML_OR_PATH_TO_SVG, dx, dy, dw, dh);
This comes from the link Simon provides above, which has a number of other suggestions and points out that you want to either link to, or download canvg.js and rgbcolor.js. These allow you to manipulate and load an SVG, either via URL or using inline SVG code between svg tags, within JavaScript functions.
Something to add, to show the svg correctly in canvas element add the attributes height and width to svg root element, Eg:
<svg height="256" width="421">...</svg>
Or
// Use this if to add the attributes programmatically
const svg = document.querySelector("#your-svg");
svg.setAttribute("width", `${width}`);
svg.setAttribute("height", `${height}`);
For more details see this
As vector graphics are meant to be potentially scaled, I will offer a method I have made that is as similar to SVG as possible. This method supports:
A resizable canvas
Transparency
Hi-resolution graphics (automatically, but no pinch support yet)
Scaling of the SVG in both directions! (To do this with pixels, you will have to divide the new length by the old one)
This is done by converting the SVG to canvas functions here, then adding that to svgRed() (after changing the name of ctx to ctx2. The svgRed() function is used on startup and during pixel ratio changes (for example, increasing the zoom), but not before the canvas is scaled (in order to increase the size of the image). It converts the result into an Image, and can be called any time by ctx.drawImage(redBalloon, Math.round(Math.random() * w), Math.round(Math.random() * h)). To clear the screen, use ctx.clearRect(0, 0, w, h) to do so.
Testing this with the SVG, I found that this is many times faster, as long as the zoom is not set to large values (I discovered that a window.devicePixelRatio of 5 gives just over twice the speed as an SVG, and a window.devicePixelRatio of 1 is approximately 60 times faster).
This also has the bonus benefit of allowing many "fake SVG" items to exist simultaneously, without messing with the HTML (this is shown in the code below). If the screen is resized or scaled, you will need to render it again (completely ignored in my example).
The canvas showing the result is scaled down (in pixels) by the devicePixelRatio, so be careful when drawing items! Scaling (with ctx.scale() this canvas will result in a potentially blurry image, so be sure to account for the pixel difference!
NOTE: It seems that the browser takes a while to optimize the image after the devicePixelRatio has changed (around a second sometimes), so it may not be a good idea to spam the canvas with images immediately, as the example shows.
<!DOCTYPE html>
<html>
<head lang="en">
<title>Balloons</title>
<style>
* {
user-select: none;
-webkit-user-select: none;
}
body {
background-color: #303030;
}
</style>
</head>
<body>
<canvas id="canvas2" style="display: none" width="0" height="0"></canvas>
<canvas id="canvas"
style="position: absolute; top: 20px; left: 20px; background-color: #606060; border-radius: 25px;" width="0"
height="0"></canvas>
<script>
// disable pinches: hard to implement resizing
document.addEventListener("touchstart", function (e) {
if (e.touches.length > 1) {
e.preventDefault()
}
}, { passive: false })
document.addEventListener("touchmove", function (e) {
if (e.touches.length > 1) {
e.preventDefault()
}
}, { passive: false })
// disable trackpad zooming
document.addEventListener("wheel", e => {
if (e.ctrlKey) {
e.preventDefault()
}
}, {
passive: false
})
// This is the canvas that shows the result
const canvas = document.getElementById("canvas")
// This canvas is hidden and renders the balloon in the background
const canvas2 = document.getElementById("canvas2")
// Get contexts
const ctx = canvas.getContext("2d")
const ctx2 = canvas2.getContext("2d")
// Scale the graphic, if you want
const scaleX = 1
const scaleY = 1
// Set up parameters
var prevRatio, w, h, trueW, trueH, ratio, redBalloon
function draw() {
for (var i = 0; i < 1000; i++) {
ctx.drawImage(redBalloon, Math.round(Math.random() * w), Math.round(Math.random() * h))
}
requestAnimationFrame(draw)
}
// Updates graphics and canvas.
function updateSvg() {
var pW = trueW
var pH = trueH
trueW = window.innerWidth - 40
trueH = Math.max(window.innerHeight - 40, 0)
ratio = window.devicePixelRatio
w = trueW * ratio
h = trueH * ratio
if (trueW === 0 || trueH === 0) {
canvas.width = 0
canvas.height = 0
canvas.style.width = "0px"
canvas.style.height = "0px"
return
}
if (trueW !== pW || trueH !== pH || ratio !== prevRatio) {
canvas.width = w
canvas.height = h
canvas.style.width = trueW + "px"
canvas.style.height = trueH + "px"
if (prevRatio !== ratio) {
// Update graphic
redBalloon = svgRed()
// Set new ratio
prevRatio = ratio
}
}
}
window.onresize = updateSvg
updateSvg()
draw()
// The vector graphic (you may want to manually tweak the coordinates if they are slightly off (such as changing 25.240999999999997 to 25.241)
function svgRed() {
// Scale the hidden canvas
canvas2.width = Math.round(44 * ratio * scaleX)
canvas2.height = Math.round(65 * ratio * scaleY)
ctx2.scale(ratio * scaleX, ratio * scaleY)
// Draw the graphic
ctx2.save()
ctx2.beginPath()
ctx2.moveTo(0, 0)
ctx2.lineTo(44, 0)
ctx2.lineTo(44, 65)
ctx2.lineTo(0, 65)
ctx2.closePath()
ctx2.clip()
ctx2.strokeStyle = '#0000'
ctx2.lineCap = 'butt'
ctx2.lineJoin = 'miter'
ctx2.miterLimit = 4
ctx2.save()
ctx2.beginPath()
ctx2.moveTo(0, 0)
ctx2.lineTo(44, 0)
ctx2.lineTo(44, 65)
ctx2.lineTo(0, 65)
ctx2.closePath()
ctx2.clip()
ctx2.save()
ctx2.fillStyle = "#e02f2f"
ctx2.beginPath()
ctx2.moveTo(27, 65)
ctx2.lineTo(22.9, 61.9)
ctx2.lineTo(21.9, 61)
ctx2.lineTo(21.1, 61.6)
ctx2.lineTo(17, 65)
ctx2.lineTo(27, 65)
ctx2.closePath()
ctx2.moveTo(21.8, 61)
ctx2.lineTo(21.1, 60.5)
ctx2.bezierCurveTo(13.4, 54.2, 0, 41.5, 0, 28)
ctx2.bezierCurveTo(0, 9.3, 12.1, 0.4, 21.9, 0)
ctx2.bezierCurveTo(33.8, -0.5, 45.1, 10.6, 43.9, 28)
ctx2.bezierCurveTo(43, 40.8, 30.3, 53.6, 22.8, 60.2)
ctx2.lineTo(21.8, 61)
ctx2.fill()
ctx2.stroke()
ctx2.restore()
ctx2.save()
ctx2.fillStyle = "#f59595"
ctx2.beginPath()
ctx2.moveTo(18.5, 7)
ctx2.bezierCurveTo(15.3, 7, 5, 11.5, 5, 26.3)
ctx2.bezierCurveTo(5, 38, 16.9, 50.4, 19, 54)
ctx2.bezierCurveTo(19, 54, 9, 38, 9, 28)
ctx2.bezierCurveTo(9, 17.3, 15.3, 9.2, 18.5, 7)
ctx2.fill()
ctx2.stroke()
ctx2.restore()
ctx2.restore()
ctx2.restore()
// Save the results
var image = new Image()
image.src = canvas2.toDataURL()
return image
}
</script>
</body>
</html>
Try this:
let svg = `<svg xmlns="http://www.w3.org/2000/svg" ...`;
let blob = new Blob([svg], {type: 'image/svg+xml'});
let url = URL.createObjectURL(blob);
const ctx = canvas.getContext('2d');
canvas.width = 900;
canvas.height = 1400;
const appLogo = new Image();
appLogo.onload = () => ctx.drawImage(appLogo, 54, 387, 792, 960);
appLogo.src = url;
// let image = document.createElement('img');
// image.src = url;
// image.addEventListener('load', () => URL.revokeObjectURL(url), {once: true});
Note: Blob is not defined in Node.js file, This is code designed to run in the browser, not in Node.
More info here
I have a Sharepoint page in which i want to show a hierarchical diagram with boxes.According to my requirement those boxes should work as links to other sharepoint pages in the same site.
Since sharepoint's default designer tools doesn't support designing such diagrams, I created a page with html5 canvas and the element i wanted inside that.
Inside the canvas i created few boxes and lines to connect them.And i added texts inside the boxes.Then i used a mouse listener to check whether the mouse pointer hovers over a box and if so changed the pointer icon and the link to be redirected to.
I added the canvas tag inside the sharepoint page by "Edit Source" and i added the javascript part using 'Embed Code'
Now the code works perfectly in IE and Firefox.
In chrome although the boxes,lines and text are drawn according to the coordinates i gave in the code but But when i hover the mouse over them it gives different coordinates for mouse listener in different browser sizes.So the mouse pointer doesn't change at correct locations ie: over the boxes.
This doesn't happen in firefox or IE. They changes the mouse pointer when it comes over the boxes and links to the pages perfectly.
Why does it change when i use chrome?
And why does it only affect to the mouse listener coordinates.
This is the code i used.(I have removed the repetitive parts which draws other boxes)
Same in jsfiddle
<canvas id="myCanvas" height="500" width="960" style="border: 1px solid;"><img src="" alt=""/> </canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx;
var rNBDX = 50; var rNBDY = 150;
var rectWidth = 200;
var rectHeight = 100;
var cornerRadius = 20;
var linkNBD="https://google.com";
var textNBD1 ="Google";
var linkHeight=20;
var linkNum = 0;
function draw(){
canvas = document.getElementById("myCanvas");
if(canvas.getContext){
ctx=canvas.getContext("2d");
//Drawing Lines
ctx.lineWidth = 3;
ctx.strokeStyle = '#000000';
ctx.moveTo(380, 100);
ctx.lineTo(380, 125);
ctx.stroke();
//Drawing Rectangles
ctx.fillStyle="#0b61d0";
ctx.strokeStyle="#0b61d0";
ctx.lineJoin = "round";
ctx.lineWidth = cornerRadius;
ctx.strokeRect(rNBDX+(cornerRadius/2), rNBDY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
ctx.fillRect(rNBDX+(cornerRadius/2), rNBDY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
//Drawing the Texts
ctx.font='24px Segoe UI Light';
ctx.fillStyle = "#FFFFFF";
ctx.fillText(textNBD1,(rNBDX+rectWidth/2)-(ctx.measureText(textNBD1).width)/2,rNBDY+rectHeight/2);
//Add mouse listeners
canvas.addEventListener("mousemove", on_mousemove, false);
canvas.addEventListener("click", on_click, false);
}
}
function on_mousemove (ev) {
var x, y;
if (ev.layerX || ev.layerX == 0) {
x = ev.layerX;
y = ev.layerY;
}
x-=canvas.offsetLeft;
y-=canvas.offsetTop;
if(x>=rNBDX && x <= (rNBDX + rectWidth) && y>=rNBDY && y<= (rNBDY+rectHeight)){
document.body.style.cursor = "pointer";
linkNum=1;
}
else{
document.body.style.cursor = "";
}
}
function on_click(e) {
switch (linkNum)
{
case 1:
window.location = linkNBD;
break;
}
}
draw();
</script>
Try adjusting the mouse coordinates like this:
function on_mousemove (ev) {
var x, y,
rect = canvas.getBoundingClientRect();
x = ev.clientX - rect.left + 1;
y = ev.clientY - rect.top + 1;
...
You will have to add (as in the example) the width of the left/top border though as getBoundingClientRect does not include those (you can calculate this dynamically using getComputedStyle and getPropertyValue of that for the borders).
I have some HTML 5 code that starts out with two black canvases and then updates the canvases one row at a time by drawing a red line for each row. It is supposed to look like a red curtain dropping down over the black background.
The odd part is that at first it looks like random rows (say 5%) are being skipped and left black. I thought I had a bug. But then I noticed that if I resized the browser window that the black lines all turned red. So then I thought it was some sort of browser repaint bug. Then I noticed that both Chrome and Safari have this behavior.
So do you think both these browsers have the same bug or am I doing something wrong?
Interestingly, this issue goes away if both canvases use style="width:100%; height:100%".
<html>
<table>
<tr>
<td><canvas id="myCanvas1" style="width:100%; height:100%"></canvas></td>
<td><canvas id="myCanvas2" style="width:200px; height:400px"></canvas></td>
</tr>
</table>
<script type="text/javascript">
var canvases = [document.getElementById("myCanvas1"), document.getElementById("myCanvas2")];
for(var i = 0; i < canvases.length; i++) {
var canvas = canvases[i];
var ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function updateRow(rowNum) {
var done = true;
for(var i = 0; i < canvases.length; i++) {
var canvas = canvases[i];
if(rowNum >= canvas.height) {
return;
}
done = false;
var ctx = canvas.getContext("2d");
var imageDataForRow = ctx.getImageData(0, rowNum, canvas.width, 1);
for(var offset = 0; offset < imageDataForRow.data.length; offset += 4) {
imageDataForRow.data[offset] = 255;
imageDataForRow.data[offset + 1] = 0;
imageDataForRow.data[offset + 2] = 0;
imageDataForRow.data[offset + 3] = 255;
}
ctx.putImageData(imageDataForRow, 0, rowNum);
}
if(!done) {
var paintNextRow = function() {
updateRow(rowNum + 1);
}
setTimeout(paintNextRow, 1);
}
}
updateRow(0);
</script>
This is your problem:
<canvas id="myCanvas1" style="width:100%; height:100%">
You are setting the width and height of the Canvas using CSS width and height and not the <canvas> html attributes. You are literally stretching a canvas sized 300x150 (the default) to skew across the entire screen.
You need to define the width/height either in the canvas tag:<canvas width="500" height="500">
or in code:
var canvas = document.getElementById("canvas");
canvas.width = 500;
canvas.height = 500;
And not by CSS. If you did this:
<canvas style="width: 500px; height: 500px;">
Then you would have a 300x150 canvas (the default size) that was scaled/warped to be 500x500, which is almost certainly what you're getting.
It looks like you want to have the canvas width change dynamically. This is OK, but you have to do one of a few things. One would be to use the window's onresize event, and set the canvas width equal to the div's clientWidth every time this happens (if the clientWidth has changed of course). The other would be to do a simple general timer to check for the same thing every half-second or so, and set canvas.width equal to the div.style.clientWidth.
(I wrote the above freehand so there might be a typo, but you get the idea)