I'm using fabricjs, and when I try to resize the whole canvas, I'm having troubles with some things. This is part of my code now:
<div id="cont2" class="cont2">
<div id="cont" class="container-div-canvas">
<canvas id="c" width="500" height="428"></canvas>
<div class="backgroundimage-div" id="bckimg-div"></div>
</div>
</div>
<script>
document.getElementById("resizecanvasbtn").onclick = () => {
document.getElementById("cont").style.width = "1000px";
document.getElementById("cont").style.height = "856px";
document.getElementById("cont2").style.width = "1000px";
document.getElementById("cont2").style.height = "856px";
document.getElementById("c").width = 1000;
document.getElementById("c").height = 856;
document.getElementById("c").style.width = "1000px";
document.getElementById("c").style.height = "856px";
document.getElementById("bckimg-div").style.width = "1000px";
document.getElementById("bckimg-div").style.height = "856px";
}
</script>
This is the canvas:
This is the canvas when I resize it:
And this is the result when I try to move a component that has been aded before the canvas resizing (the whole glitch is the result of when I move one element arround the canvas):
When I add an element after I resize the canvas, it automatically appears on another z-index (I think) and I can't even move it.
I'm not sure where I have the problem, or which thing I'm not having on mind when resizing the canvas.
Any suggestions on where to begin?
Thanks
It results that the document.getElementById("c").width = 1000; does not change the resolution of the canvas at all.
Instead how it needs to be done is with:
canvas.setWidth(1000);
That will change not only the canvas but the canvas-container and the upper-canvass divs that fabricjs generates automatically, which leads on the canvas size.
Hope it helps anybody:)
Related
I wonder if I make a Flash HTML5 Canvas Banner at 300x250 to start, how can I make it expand down the page and extend the canvas size?
Try this:
var originalHeight = this.stage.canvas.height;
var expandedHeight = 300; // Put here your desired height
this.stage.addEventListener("mouseover", mouseOverHandle);
function mouseOverHandle() {
this.stage.canvas.height = expandedHeight;
this.stage.addEventListener("mouseleave", mouseLeaveHandle);
this.stage.removeEventListener("mouseover", mouseOverHandle);
}
function mouseLeaveHandle() {
this.stage.canvas.height = originalHeight;
this.stage.addEventListener("mouseover", mouseOverHandle);
this.stage.removeEventListener("mouseleave", mouseLeaveHandle);
}
You will see some flickering, because the canvas redraw their content on resize. See this question for more info Flickering during resizing of HTML5 canvas
Click on the canvas then there properties tab and FPS ,Size in then change size to if you want size
I am trying to write a image magnifier with canvas, It works fine but the problem is when I mouseover the image the image drawn on the canvas is not positioned correctly based on the mouse position on the image.
You can see the problem here
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Magnifier</title>
<style type="text/css">
canvas
{
border:1px solid #000;
width:150px;
height:150px;
border-radius:80px;
position:absolute;
}
</style>
</head>
<body>
<img src="natural.jpg" height="400" id="image" width="600" onMouseMove="move(event);">
<canvas id="magnifier"></canvas>
<input type="button" name="magnify" id="magnify" value="magnify" onClick="magnify()" />
</body>
</html>
<script type="text/javascript">
var flag = 0;
var lens = document.getElementById('magnifier');
var img = document.getElementById('image');
var ctx=lens.getContext("2d");
function magnify()
{
flag = 1;
}
function move(e)
{
if(flag == 1)
{
var co_ord = getImageCoords(e,img);
var x = co_ord['x']+img.offsetLeft;
var y = co_ord['y']+img.offsetTop;
draw(x,y);
/*lens.style.top = y+"px";
lens.style.left = x+"px";*/
}
}
function getImageCoords(event,img) {
var cords = new Array;
cords['x'] = event.offsetX?(event.offsetX):event.pageX-img.offsetLeft;
cords['y'] = event.offsetY?(event.offsetY):event.pageY-img.offsetTop;
return cords;
}
function draw(a,b)
{
ctx.clearRect(0,0,lens.width,lens.height);
ctx.drawImage(img,a,b,150,150,0,0,300,150);
}
</script>
The main problem is that you are scaling the original image by forcing it into a 640 by 400 img element. The actual image is quite a bit larger. The width and height are just presentation settings, so when you draw the image ctx.drawImage(img,a,b,150,150,0,0,300,150); it is drawing the original unsized image. This means that your mouse coordinates do not match the location on the original image.
I can see two options:
1. Resize original by drawing to canvas
See update here. I haven't used cssdeck before, so here is a fiddle in case I didn't save it properly. Basically, it resizes the image to a canvas (resizeCanvas) and then uses this canvas for the drawing:
Relevant HTML:
<canvas id='resizeCanvas' height='400' width='600' onMouseMove="move(event);"></canvas>
Relevant JavaScript:
var ctxR=resizeCanvas.getContext("2d");
ctxR.drawImage(theImg,0,0,600,400);
There were a few other tweaks I made. First, you should specify the width and height attributes directly on the magnifier canvas. Otherwise, if this is different from the css then this will cause scaling. Then you can do the scaling to double the size by:
ctx.drawImage(img,a,b,150,150,0,0,300,300);
The only drawback of that approach is that you have a high res image that you are uploading and then losing some quality when you maginify which seems a pity. So, a better approach might be to load the original image without adding to the dom and then translate the x,y coords appropriately for the original image. Which is the second approach:
2. Scale x,y coordinates (better quality)
See the update here (fiddle here as well). As you can see, the quality is much better.
Here, we load the original image:
var origImage = document.createElement('img');
var origImage.src = '<image source>'
Then, just scale accordingly:
scaleX = origImage.width/img.width;
scaleY = origImage.height/img.height;
ctx.clearRect(0,0,lens.width,lens.height);
ctx.drawImage(img,a*scaleX,b*scaleY,150,150,0,0,150,150);
Note that we are not actually doing any resizing of the original image at all when we draw it to the canvas (width and height are 150 in all cases), instead we are just showing it at its larger native size. For smaller images, you may want to resize according to some fudge factor.
I'm creating a HTML5 game which can be made to run on Android also. I went through few articles and didn't get the solution yet. I have a image which i'm generating through javascript and I want to move this image using touchmove so that I can run it in my Android device. This is the code:
gameCanvas.addEventListener("touchmove", touchXY, true);
function touchXY(e) {
if (!e)
var e = event;
e.preventDefault();
avatarX = e.targetTouches[0].pageX - gameCanvas.offsetLeft;
avatarY = e.targetTouches[0].pageY - gameCanvas.offsetTop;
}
This is not working. I got this code from https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/HTML-canvas-guide/AddingMouseandTouchControlstoCanvas/AddingMouseandTouchControlstoCanvas.html
And this is my canvas:
<canvas id="gameCanvas" onclick="setUpGame();" width="400" height="300"></canvas>
This is my image:
avatarImage.src = "img/avatar.png";
gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
I just want to move the image inside the canvas.
I wrote a full example, hopefully it's not too verbose.
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
</head>
<body>
<canvas id='canvas' width='512' height='512'></canvas>
<script>
var c=document.getElementById('canvas'),
ctx=c.getContext('2d'),
activeBox='none',
//populate the map with objects
box=[
{
x:256,
y:128,
width:32,
height:64
},
{
x:128,
y:64,
width:64,
height:64
},
{
x:32,
y:32,
width:32,
height:32
},
];
function draw(){
//clear the screen, draw population
ctx.clearRect(0,0,c.width,c.height);
for(var i=0;i<box.length;i++){
ctx.fillRect(box[i].x,box[i].y,box[i].width,box[i].height);
}
//repeat at 60fps if possible, pause if window looses focus
requestAnimationFrame(draw);
}
function startTouch(e){
//this makes it easier to write control flow later and keeps XY relative to canvas
var xTouch=e.touches[0].pageX-c.offsetLeft,
yTouch=e.touches[0].pageY-c.offsetTop;
//its best to go through this loop in touchstart, because it only happens once per touch
for(var i=0;i<box.length;i++){
if(xTouch>box[i].x&&xTouch<box[i].x+box[i].width){
if(yTouch>box[i].y&&yTouch<box[i].y+box[i].height){
activeBox=i;
}
}
}
}
function moveTouch(e){
//grab a box by the center
if(activeBox!='none'){
box[activeBox].x=e.changedTouches[0].pageX-box[activeBox].width/2;
box[activeBox].y=e.changedTouches[0].pageY-box[activeBox].height/2;
}
}
function endTouch(){
//clear active so that dragging empty space wont move the last active box
activeBox='none';
}
canvas.addEventListener('touchstart',startTouch);
canvas.addEventListener('touchmove',moveTouch);
canvas.addEventListener('touchend',endTouch);
window.addEventListener('load',draw);
</script>
</body>
</html>
I used fillRect for simplicity, but if you want to replace it with drawImage you'll need to create a new element for each and add a source property to the box object array. Here's a partial example.
//you need a new one of these for every image
var img=new Image();
img.src='http://www.w3schools.com/images/w3logotest2.png';
var box={
source:img,
x:Math.floor((Math.random()*256)+1),
y:Math.floor((Math.random()*256)+1)
};
//make sure the image doesnt load before the script
window.onload=function(){
ctx.drawImage(img,box.x,box.y);
}
I created a jsfiddle for my current code. http://jsfiddle.net/gL5sB/38/
I am trying to change the body background css on scroll event. When the background changes it appears to flicker when the css is updated and new image is loaded. At times it seems smooth and then it seems to get worse. Very strange. Curious if anyone knows how to optimize?
I am preloading the images. Not sure why the flicker. Any ideas?
$(document).ready(function () {
switchImage();
});
$(window).scroll(function () {
switchImage();
});
var pics = []; // CREATE PICS ARRAY
//PRELOAD FUNCTION TO SET UP PICS ARRAY IN MEMORY USING IMAGE OBJECT
function preload() {
for (i = 0; i < arguments.length; i++) {
pics[i] = new Image();
pics[i].src = arguments[i];
//alert("preload " + arguments[i]);
}
}
preload(
'bgImage/100.jpg',
'bgImage/101.jpg',
'bgImage/102.jpg',
'bgImage/103.jpg',
'bgImage/104.jpg',
'bgImage/105.jpg',
'bgImage/106.jpg',
'bgImage/107.jpg',
'bgImage/108.jpg',
'bgImage/109.jpg',
'bgImage/110.jpg',
'bgImage/111.jpg',
'bgImage/112.jpg',
'bgImage/113.jpg'
);
function switchImage() {
var s = $(window).scrollTop()/10;
var index = Math.floor(s / 5);
$('body').css('background-image', 'url(' + pics[index].src + ')');
}
Why don't you use one image (sprite image) and just move it with background-position instead of replacing the image?
(about the size - you can set percentage based background-size in your case - the height will be 1400%
Because your'e preloading all the images anyway - it won't cost you in page loading time - and it might also save some time because with the right compression 1 image of 14 will weight less then 14 images of 1
Chrome is OK. I can see the flicker in IE. A Solution for that is at the bottom of this post.
I suspect A video version would compress and load faster than all the images, but as suggested by #Allendar drawing it would be the most transmission efficient. I would suggest canvas or SVG.
Another way using images along would be to have individual components as either images or icon fonts placed on the display with absolute positioning and then just turning them on or off in script. But that's a very complex solution.
I think the simplest and fastest method for you to solve the issue today though would be to just tweak the solution you have. As other people have suggested, the multiple images approach won't be super efficient and if you're going to take that route at least make sure you set the caching headers on your web server to cache indefinitely;
Cache-Control: public;
Expires: Mon, 31 Dec 2035 12:00:00 GMT
OK, so the flicker problem is just a bug/inefficiency in the rendering engine in IE, so here's a workaround that uses a different approach.
Basically stretch an absolutely positioned DIV over the body, instead of using the body itself. In fact, in this case we use multiple DIVs, one for each image and overlay them on top of one another. You could create these nodes in script as well.
Secondly, add another DIV for your content, and overlay that over the body as well;
<body>
<div id="b100" class="background" style="background-image:url('http://ingodwetrustthemovie.com/bgImage/100.jpg')"></div>
<!-- rest omitted -->
<div id="b113" class="background" style="background-image:url('http://ingodwetrustthemovie.com/bgImage/113.jpg')"></div>
<div id="content">
<div id="test">test div</div>
here is some text
</div>
</body>
The simply show one at a time and hide the rest;
function switchImage() {
var s = $(window).scrollTop()/10;
var index = Math.floor(s / 5);
$('.background').hide();
$('.background').eq(index).show();
}
I had a suspicion that twiddling the css display option would be less flickery than changing the src attribute and it seems to do the trick.
Here's the Fiddle
Of course you may still need to optimise the code to allow for the first loaded image to be shown first instead of the plain background, but I think this demonstrates the principle of a fix.
You could also consider making a very large CSS Sprite, by bundling the images into one huge strip and then using background-position. That would probably work on the body tag itself then. Of course this would mean downloading a huge file before you can display any images at all, but there are two advantages;
One image (especially with such similarity) will compress way better than each individual one in isolation.
Using the same caching directives, that's only one HTTP/GET/302 cycle instead of 13 once you've fetched the image the first time, so your page may load faster still.
SVG
SVG elements work much like the DOM. If you can get your content delivered as an SVG you can drill into the graphic, locate the elements, give them IDs etc, and manipulate them much like you would any other DOM element;
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<ellipse id="e1" cy="420" cx="200" rx="420" ry="30" style="fill:3f5566" />
<ellipse id="e2" cy="420" cx="170" rx="390" ry="20" style="fill:4f5566" />
<ellipse id="e3" cy="420" cx="145" rx="370" ry="15" style="fill:5f5566" />
<ellipse id="e4" cy="420" cx="100" rx="370" ry="20" style="fill:6f5566" />
<ellipse id="e5" cy="420" cx="45" rx="300" ry="15" style="fill:8f5566" />
</svg>
Here's another fiddle that hides/unhides SVG elements based on a scroll.
Ideally, assuming they've generated that graphic in 'layers', try and have your designers deliver you an SVG where the layers are converted into groups. Adobe Illustrator can do that for instance.
Then you can easily turn off the layers/groups as necessary to create the animated effect.
If the images aren't really needed, you can create some system for yourself where you draw, and keep updating, the background using canvas. This would be start;
JSfiddle
HTML
<img id="template" src="http://ingodwetrustthemovie.com/bgImage/100.jpg" />
<canvas id="absBg" width="1000px" height="560px"></canvas>
CSS
body {
margin: 0px;
}
#template {
position: absolute;
width: 1000px;
height: 563px;
}
#absBg {
position: absolute;
/*background: rgba(0, 0, 0, 0.50);*/
/*height: 100%;*/
}
JavaScript/jQuery
'use strict';
function drawBlock(ctx, color, line_width, start, lines) {
ctx.lineWidth = line_width;
ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(start[0], start[1]);
for (var i = 0; i < lines.length; i++) {
ctx.lineTo(lines[i][0], lines[i][1]);
}
ctx.closePath();
ctx.stroke();
}
function drawBg() {
var absBg = document.getElementById('absBg');
var ctx = absBg.getContext('2d');
var demo_red = 'red';
var grey = '#28282';
var color = demo_red;
drawBlock(ctx, color, 1, [185, 87], [[205, 75], [226, 98], [207, 110]]);
drawBlock(ctx, color, 1, [235, 60], [[253, 50], [272, 71], [254, 81]]);
}
$(document).ready(function () {
drawBg();
// Scroll trigger
$(window).scroll(function () {
});
});
The demo uses the actual background image you have as a background-model, where you can draw over with the canvas. This way you can mimick the look of the original image you have.
You can try to manage some kind of "difference-array" where you store which blocks are different on which locations. This way you can trigger the function on scroll with certain parameters to let it change the drawing based on that.
I hope for you that you don't "need" the images per se. Drawing with canvas is so much faster than loading tons of images :)
Here is a solution that works (2014.7.11) at firefox 30.0, chrome 35.0, opera 22.0, ie 11.0:
STEP 1: add these lines at .htaccess:
# cache for images
<FilesMatch "\.(png)$">
Header set Cache-Control "max-age=10000, public"
</FilesMatch>
detailed description of this problem and how to fix it:
https://code.google.com/p/chromium/issues/detail?id=102706
STEP 2: add images preloading, for example:
var pics = []; // CREATE PICS ARRAY
$(document).ready(function(){
...
preload(
'/public/images/stars.red.1.star.png',
'/public/images/stars.red.2.star.png',
'/public/images/stars.red.3.star.png',
'/public/images/stars.red.4.star.png',
'/public/images/stars.red.5.star.png',
'/public/images/stars.empty.png'
);
...
$('.rating').on('mousemove', function(event){
var x = event.pageX - this.offsetLeft;
var id = getIdByCoord(x); //
if ($(this).data('current-image') != id) {
$(this).css('background-image', 'url(' + pics[id].src + ')');
$(this).data('current-image', id);
}
})
...
})
...
// PRELOAD FUNCTION TO SET UP PICS ARRAY IN MEMORY USING IMAGE OBJECT
function preload() {
for (i = 0; i < arguments.length; i++) {
pics[i] = new Image();
pics[i].src = arguments[i];
// alert("preload " + arguments[i]);
}
}
P.S. thanks Shawn Altman
I need to get a background on canvas using layers. Variable for it's background. I know I should use CSS and set the z-index, but do not know how to do it in this case.
JS:
function doFirst(){
var x = document.getElementById('canvas');
canvas = x.getContext('2d');
var item1 = new Image();
item1.src = "images/sheep.png";
item1.addEventListener("load", function() { canvas.drawImage(item1,20,300)}, false);
var item2 = new Image();
item2.src = "images/tshirt.png";
item2.addEventListener("load", function() { canvas.drawImage(item2,300,300)}, false);
var background = new Image();
background.src = "images/background.png";
background.addEventListener("load", function() { canvas.drawImage(background,0,0,1024,768)}, false);
}
HTML:
<canvas id="canvas" width="1024" height="768">
The Canvas element is not designed to address layers within the element, you'll need to approach your problem using multiple canvas elements instead.
Here's a good article to assist you with the approach
If you're wanting to display multiple images on the same layer (canvas) then to order the images just draw them to the screen in the order you want them to display. The first drawn images will be at the bottom with each draw drawing on top.
If you want to treat layers as individual canvases, you can use css to set the z-index, or use Javascript like so:
canvas.style.zIndex = 99;