I'm using RaphaëlJS for rotating an image forever. You can see the current result here. However, I'm experiencing some performance issues after resizing the browser window many times.
Could it be that new animations are created every time the window is resized while old ones are not removed? If that's the case, how can I stop and remove them? If not the case, any idea about how I could improve the performance?
The code is this:
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
background-color: black;
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
</style>
<script type="text/javascript" src="raphael.js"></script>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
var canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var gearImageSrc = "images/gear.png";
var nikaImageSrc = "images/nika_half.png";
var logoImageSrc = "images/logo_mp.png";
var paper;
var gearImage = new Image();
var nikaImage = new Image();
var logoImage = new Image();
gearImage.src = gearImageSrc;
nikaImage.src = nikaImageSrc;
logoImage.src = logoImageSrc;
showAll = function () {
paper = Raphael(0, 0, canvas.width,canvas.height);
gear = paper.image(gearImageSrc, canvas.width/2 - gearImage.width/4, canvas.height/2 - gearImage.height/3, gearImage.width/2, gearImage.height/2);
nika = paper.image(nikaImageSrc, canvas.width/2 - nikaImage.width/4, canvas.height/2 - nikaImage.height/3, nikaImage.width/2, nikaImage.height/2);
logo = paper.image(logoImageSrc, canvas.width/2 - logoImage.width/4, canvas.height/2 + gearImage.height/6, logoImage.width/1.5, logoImage.height/1.5);
var spin = Raphael.animation({transform: "r-360"}, 10000).repeat(Infinity);
gear.animate(spin);
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
paper.remove();
showAll();
}
window.addEventListener('resize', resizeCanvas, false);
window.onload = function () {
showAll();
}
</script>
</body>
</html>
I might have a solution for it, just let me know if this works:
var spin = Raphael.animation({transform: "...r-360"}, 10000).repeat(Infinity);
You have to add ... in your transform() whenever you want to append to your transform, which you seem to be doing by re-sizing the window.
Related
i was testing some code with javascript, about resizing the html5 canvas. i've asked similar question before, when i was testing the same thing with jquery. and i've got the desired solution that time, reference (eg_1). in this (eg_1), the result was, canvas element was resizing in every frame because of requestAnimationFrame($.fn.animate); is taking place in a recursive form.the visual result for that is here the width attribute updates when i resize the console window. and if i close the console the canvas resizes automatically, i don't need to reload the window, to update the attribute values. means canvas is full screen, all time. but i am having problem with the same thing, in javascript. if i close the console, the width of the console cuts (or resizes) the canvas with a white space. here is the visual result the white space is my problem. i've to reload the window, to make the canvas full screen again. in short i'm not having the same effect as (eg_1). here i tried so far, reference (eg_2).
(eg_1)
html : <canvas></canvas>
css :
* {
margin: 0;
padding: 0;
overflow: hidden;
}
canvas {
background-color: turquoise;
}
jquery :
$(document).ready(function() {
var view = $(window),
canvas = $("canvas"),
ctx = canvas[0].getContext("2d"),
width = view.width(),
height = view.height();
$.fn.windowResize = function() {
var width = view.width();
height = view.height();
canvas.attr("width", width);
canvas.attr("height", height);
};
view.on("resize", $.fn.windowResize);
canvas.attr("width", width);
canvas.attr("height", height);
$.fn.animate = function() {
requestAnimationFrame($.fn.animate);
ctx.clearRect(0, 0, width, height);
// start here
};
$.fn.animate();
});
(eg_2)
var canvas = document.querySelectorAll("canvas")[0];
var context = canvas.getContext("2d");
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
function resize() {
setInterval(resize, 5);
clearInterval(resize);
canvas.width = windowWidth;
canvas.height = windowHeight;
}
resize();
Answer:
Your initial issue:
You need to get the updated values of window.innerWidth and window.innerHeight.
To do this you can turn the variables into functions that request the current values.
Example:
var canvas = document.querySelectorAll("canvas")[0];
var context = canvas.getContext("2d");
var windowWidth = () => window.innerWidth;
var windowHeight = () => window.innerHeight;
function resize() {
setInterval(resize, 5);
clearInterval(resize);
canvas.width = windowWidth();
canvas.height = windowHeight();
}
resize();
* {
margin: 0;
padding: 0;
overflow: hidden;
}
canvas {
background-color: turquoise;
}
<canvas></canvas>
Other issues:
Your use of Timers is incorrect. clearInterval(resize) isn't going to clear anything, because resize is not a timer itself.
Secondly you shouldn't be using any timers when rendering to Canvas. It can often lead to things like the above( multiple timers ) or issues with constant, unnecessary, re-processing.
Instead of resizing perpetually you may want to just do so when the resize event is dispatched. Even if you were using requestAnimationFrame - which would effectively stop the render if anything else takes precedence or if you switch tabs, it's an unnecessary process in that you're constantly re-rendering the same thing without cause. Canvas Apps tend to get big real quick when you add in multiple rendering functions, so you'll want to mitigate unnecessary steps.
You can do this by attaching to appropriate events and creating requestAnimationFrame helpers like in the following example:
var canvas = document.querySelectorAll("canvas")[0];
var context = canvas.getContext("2d");
var windowWidth = () => window.innerWidth;
var windowHeight = () => window.innerHeight;
var request = fn => requestAnimationFrame(fn),
requestHandler = fn => () => request(fn);
function resize() {
canvas.width = windowWidth();
canvas.height = windowHeight();
}
request(resize);
window.addEventListener("resize", requestHandler(resize) );
* {
margin: 0;
padding: 0;
overflow: hidden;
}
canvas {
background-color: turquoise;
}
<canvas></canvas>
I have an image that covers the entire width and height of a canvas that covers the entire screen. The problem is, when the code is run on a computer with a different resolution (width and height), the image does not adjust to the proper height and width of that screen. It stays the original size that I set it in JS. I'm very new to JavaScript, so any help I could get to this image to resize on different resolutions would be amazing. Thanks!
//Global Canvas
var canvas = document.querySelector('.canvas1');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var c = canvas.getContext('2d');
//Functions
function load_image1() {
"use strict";
var base_image;
base_image = new Image();
base_image.src = ['https://postimg.cc/tnGDb1vD'];
base_image.onload = function(){
c.drawImage(base_image, (window.innerWidth - 1700), -100, 1920, 1080);
};
}
#charset "utf-8";
/* CSS Document */
body {
margin: 0;
}
.canvas1 {
margin: 0;
display: block;
}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Sylvanas</title>
<link href="styles.css" rel="stylesheet" type="text/css">
</head>
<body>
<canvas class="canvas1">
</canvas>
<!--<canvas class="canvas2">
</canvas>-->
<script src="script.js"></script>
</body>
</html>
You can adjust some c.drawImage() params to get what you're after using some window properties.
Edit: As pointed out in a comment below I added some additional code to listen for a window resize which will adjust the dimensions of the canvas image as needed. This solution does flash a bit on my screen when resizing so there is a potential drawback.
c.drawImage(base_image, 0, 0, canvas.width, canvas.height);
JSFiddle: http://jsfiddle.net/gucr5m1b/4/
var canvas = document.querySelector('.canvas1');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var c = canvas.getContext('2d');
function load_image1() {
"use strict";
var base_image = new Image();
base_image.src = ['http://i.imgur.com/8rmMZI3.jpg'];
base_image.onload = function() {
c.drawImage(base_image, 0, 0, canvas.width, canvas.height);
};
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
load_image1();
}
function startCanvas() {
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
}
startCanvas();
#charset "utf-8";
/* CSS Document */
body {
margin: 0;
}
.canvas1 {
margin: 0;
display: block;
}
<canvas class="canvas1"></canvas>
I am not a real programmer but have done some small projects in JavaScript. I am now working on a small app backing up a scientific poster. The script works, but the cursor (cross) and the other field, both bit maps do not appear before the page is reloaded. Same behavior in several browsers. I found out that the onload function is not run. But the first one img is run. I don't understand why.
I did some googling and found references to the problem but did not really understand it.
Could somebody be so kind and help me?
I made a versjon with only the essential gode:
URL: http://folk.uio.no/gerald/IADMFR2017/test.html
Here is the source:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Increasing clinical relevance in oral radiology through computer-based examination?</title>
<script src="https://code.createjs.com/easeljs-0.8.2.min.js"></script>
<script>
function init() {
var canvas = document.getElementById("canvas");
var stage = new createjs.Stage(canvas);
stage.enableMouseOver();
createjs.Touch.enable( stage );
img = new Image();
img.src = 'images/radiograph.png';
img.onload = function(event) {
var bitmap = new createjs.Bitmap('images/radiograph.png');
bitmap.x = 50;
bitmap.y = 80;
bitmap.on("click", handleMouseEvent);
stage.addChild(bitmap);
stage.update();
}
fieldIm = new Image();
fieldIm.src = 'images/field.png';
var field;
fieldIm.onload = function(event) {
field = new createjs.Bitmap('images/field.png');
field.scaleX=0.9;
field.scaleY=0.9;
field.x=400;
field.y=100;
field.visible = false;
stage.addChild(field);
stage.update();
}
crossIm = new Image();
crossIm.src = 'images/cross.png';
var cross;
crossIm.onload = function(event) {
cross = new createjs.Bitmap('images/cross.png');
cross.scaleX=0.5;
cross.scaleY=0.5;
cross.zIndex = 500;
cross.visible = false;
stage.addChild(cross);
stage.update();
}
stage.update();
function handleMouseEvent(evt) {
cross.x = evt.rawX - 12;
cross.y = evt.rawY - 12;
cross.visible = true;
stage.update();
}
var rect = new createjs.Rectangle(0, 0, 180, 30);
var frg = '#FFFFFF';
var bkg = '#000000';
var container = new createjs.Container();
container.x = 450;
container.y = 500;
var rectShape = new createjs.Shape();
rectShape.graphics.c().f(bkg).dr(0, 0, rect.width, rect.height);
var text = new createjs.Text("Check Result", "30px Arial", "#FFFFFF");
container.addChild(rectShape);
container.addChild(text);
container.on("click", handleToggle);
stage.addChild(container);
stage.update();
function handleToggle(evt) {
field.visible = !field.visible;
if (field.visible) {
text.text="Hide Result";
} else {
text.text="Check Result";
}
stage.update();
}
stage.update();
}
</script>
</head>
<body onload="init()">
<H1>Poster ICDMFR 2017 Clickable task example</H1>
<canvas id="canvas" width="800" height="600"></canvas>
</body>
</html>
I appreciate any help on that.
Greetings
Gerald
http://www.odont.uio.no/english/people/adm/fac/gerald
Okay. So I have a canvas with an id of mainCanvas, with javascript and css on the same html page. I want to have it so that I can make things a certain width of the canvas, in javascript (Example, a red rectangle be the width and height of the canvas). So I have the canvas width set to 60% and the height set to 500px (the height isn't the problem) in css, and I have the code,
var canvas = document.getElementById("mainCanvas");
var context = document.getContext("2d");
var width = canvas.width;
var height = canvas.height;
which gets the canvas width and height. Then I have a setInterval() function with a game function that calls and update function, and a render function.
function game() {
update();
render();
}
function update() {
}
function render() {
context.fillStyle = "red";
context.fillRect(0, 0, width, height);
}
setInterval(function() {
game();
}, 1000/30);
So I have a rectangle that has the width variable as the width parameter, and the height variable as the height parameter.
Just so it is easier to see, here is a snippet.
<html>
<head>
<title></title>
<style type="text/css">
body {
background-color: #222222;
}
canvas {
background-color: #1f1f1f;
width: 60%;
height: 500px;
}
</style>
</head>
<body>
<canvas id="mainCanvas"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("mainCanvas");
var context = document.getContext("2d");
var width = canvas.width;
var height = canvas.height;
function game() {
update();
render();
}
function update() {
}
function render() {
context.fillStyle = "red";
context.fillRect(0, 0, width, height);
}
setInterval(function() {
game();
}, 1000/30);
</script>
</body>
</html>
Why is the width and height not working? I have seen many questions on stackoverflow but those don't fix my problem.
Change the line:
var context = document.getContext("2d");
To:
var context = canvas.getContext("2d");
Change it to:
var canvas = document.getElementById("mainCanvas");
var context = canvas.getContext("2d"); // `canvas` instead of `document`
var width = canvas.width;
var height = canvas.height;
I'm working on a proof of concept on an HTML5 canvas. I was able to get this working like a charm in Chrome and IE9, but in Firefox 4 I'm getting constant flicker as it redraws the canvas. I've tried a few techniques mentioned on this site like double buffering but I'm still getting a large amount of flicker. Any insight on this would be appreciated!
<!DOCTYPE HTML>
<html>
<head>
<style type="text/css">
body
{
margin:0px;
padding:0px;
text-align:center;
}
canvas
{
outline:0;
border:1px solid #000;
margin-top: 10px;
margin-left: auto;
margin-right: auto;
}
</style>
<script type="text/javascript">
var canvasWidth = 640;
var thisXPos = 0;
var canvasHeight = 820;
var thisYPos = 0;
var canvas = null;
var context = null;
var gLoop = null;
var rain = [];
var rainImg = "images/raindrop.gif";
var bgImg = null;
var i;
for (i = 0; i < howManyLetters; i++)
{
rain.push([Math.floor(Math.random() * canvasWidth), Math.floor(Math.random() * canvasHeight),rainImg]);
}
var DrawRain = function()
{
for (i = 0; i < 10; i++)
{
thisXPos = rain[i][0];
thisYPos = rain[i][1];
imgSrc = rain[i][2];
letterImg = new Image();
letterImg.setAtX = thisXPos;
letterImg.setAtY = thisYPos;
letterImg.onload = function()
{
context.drawImage(this, this.setAtX, this.setAtY);
}
letterImg.src = imgSrc;
}
};
var MoveRain = function(e)
{
for (i = 0; i < 10; i++)
{
if ((rain[i][1] - 5) > canvasHeight)
{
randomnumber = Math.floor(Math.random()*26);
rain[i][0] = Math.random() * canvasWidth;
rain[i][1] = 0;
rain[i][2] = rainImg;
}
else
{
rain[i][1] += e;
}
}
};
var clear = function()
{
context.beginPath();
context.rect(0, 0, canvasWidth, canvasHeight);
context.closePath();
bgImg = new Image();
bgImg.src = "images/bg.png";
bgImg.onload = function()
{
context.drawImage(bgImg,0,0);
}
}
var GameLoop = function()
{
context.save();
clear();
MoveRain(1);
DrawRain();
context.restore();
gLoop = setTimeout(GameLoop, 10);
}
function loadGame()
{
canvas = document.getElementById("gameCanvas");
context = canvas.getContext("2d");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
GameLoop();
}
</script>
</head>
<body onload="loadGame();">
<canvas id="gameCanvas"></canvas>
</body>
</html>
I have distilled your example down to this:
http://jsfiddle.net/sPm3b/6/
And it works very fast in Firefox and Chrome.
So we know that the problem lies in the images.
You need to optimize how they are created and loaded. Right now, each clear() creates a new image and waits for it to load! That image should be created only once, in your loadGame() and then reused over and over.
Same exact deal with letterImg in DrawRain(). Move the creation of it to loadGame()
That will probably fix the problem.
EDIT:
like this:
At the top add:
var letterImg = new Image();
var bgImg = new Image();
Then
function loadGame()
{
canvas = document.getElementById("gameCanvas");
context = canvas.getContext("2d");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
bgImg.src = "images/bg.png";
letterImg.src = "images/raindrop.gif";
// optional: wait for them to load here
GameLoop();
}
Then drawRain, for example, would look like this:
var DrawRain = function()
{
for (i = 0; i < 10; i++)
{
thisXPos = rain[i][0];
thisYPos = rain[i][1];
context.drawImage(letterImg, thisXPosX, thisYPos); // letterImg was already constructed, no need to make it again
}
};
In complement to Simon Sarris response. I've used a 'double canvas' technique to avoid screen fickering with heavy canvas.
The way it works is always have 2 version of the canvas, one in DOM, one outside, and always draw on the one which is not in DOM. I use it with a redraw queue.
here's a part of a working code
(...)
clear: function() {
//rotating on 2 canvas, one for draw (outside DOM) one for show
var self = this;
if (null == self.canvasbackup) {
var tmpcanvas = self.canvas.clone(true);
self.canvasbackup = self.canvas;
self.canvas=tmpcanvas;
} else {
var tmpcanvas = self.canvasbackup;
self.canvasbackup = self.canvas;
self.canvas=tmpcanvas;
}
self.ctx = self.canvas[0].getContext('2d');
self.ctx.clearRect( 0, 0, self.options.width, self.options.height );
jQuery.each(self.elements,function(idx,elt){
// custom function: my elements need to know which canvas they depends on
elt.reconnectCanvas(self.canvas,self.ctx);
});
},
inDOM: function() {
var self = this;
if(null==self.canvasbackup) {
//1st time need to get all things in DOM
self.canvas.appendTo(self.div);
self.div.appendTo(self.container);
} else {
// remove current shown canvas
self.connectHuman();
self.canvasbackup.remove();
// loosing some events here...
self.canvas.appendTo(self.div);
// div is already in DOM, we are in redraw
}
},
redraw: function() {
var self = this;
self.clear();
jQuery.each(self.elements,function(idx,elt){
elt.draw();
elt.enddraw();
});
self.inDOM();
}
(...)