One way I can think of is with an animated svg, but there is probably a better way. What would you do if you had to animate these wavy blobs (mobile compatible)
Link to the only pin I've found similar
var wave = document.createElement("div");
wave.className += " wave";
docFrag.appendChild(wave);
wave.style.left = i * waveWidth + "px";
wave.style.webkitAnimationDelay = (i / 100) + "s";
Touch interaction would be nice too. Would there be any problems with canvas stuff ?
Here's an implementation of #DA.'s good answer:
var canvas=document.getElementById('canvas');
var ctx=canvas.getContext('2d');
var cw=canvas.width;
var ch=canvas.height;
ctx.textAlign='center';
ctx.textBaseline='middle';
ctx.font='16px verdana';
ctx.lineWidth=5;
ctx.strokeStyle='white';
ctx.fillStyle='white';
var offsetX=0;
var bk=makeWave(canvas.width,canvas.height-120,10,2,'lightskyblue','cornflowerblue');
requestAnimationFrame(animate);
function animate(time){
ctx.clearRect(0,0,cw,ch);
ctx.drawImage(bk,offsetX,0);
ctx.fillStyle='white';
ctx.font='18px verdana';
ctx.fillText('Multiple Lists',cw/2,30);
ctx.strokeRect(cw/2-50,85,100,50);
ctx.fillStyle='gray';
ctx.font='12px verdana';
ctx.fillText('You can create and save multiple ...',cw/2,250);
offsetX-=1;
if(offsetX< -bk.width/2){offsetX=0;}
requestAnimationFrame(animate);
}
function makeWave(width,midWaveY,amplitude,wavesPerWidth,grad0,grad1){
var PI2=Math.PI*2;
var totValue=PI2*wavesPerWidth;
var c=document.createElement('canvas');
var ctx=c.getContext('2d');
c.width=width*2;
c.height=midWaveY+amplitude;
var grad=ctx.createLinearGradient(0,0,0,midWaveY);
grad.addColorStop(0.00,grad0);
grad.addColorStop(1.00,grad1);
//
ctx.beginPath();
ctx.moveTo(0,0);
for (x=0;x<=200;x++) {
var n=totValue*x/100;
ctx.lineTo(width*x/100,Math.sin(n)*amplitude+midWaveY);
}
ctx.lineTo(c.width,0);
ctx.closePath();
ctx.fillStyle=grad;
ctx.fill();
return(c);
}
body{ background-color:white; }
canvas{border:1px solid red; margin:0 auto; }
<canvas id=canvas width=300 height=300></canvas>
I'd make the wave a PNG (bottom solid gray, top transparent). Place it in a div twice the width of the card, and place that in a div the width of the card (this second div is the 'mask').
Then via CSS, have the nested give transform on the x axis to animate it sideways.
You shouldn't need any JS for this.
I would do this:
on page load create an off-screen canvas (just set display: none)
in a for loop compute the wave:
clear with transparency
paint only the white part because the colored part has a gradient
after each paint, get the PNG data out of the canvas and store it in an array
after the loop you will have an array of PNG images (frames)
cycle through those frames without recomputing the wave over and over again
This requires the wave to have a period that is affine to the number of frames you take (say a 2 second animation at 10 Hz would require 20 frames to be cyclic)
To be honest, you could store that server-side and just download it, without computing it client-side. Those PNG images would be very tiny because there isn't any color involved (just transparent/white/alpha channel). There are optimal settings for this, I guesstimate some 1KB per frame would suffice, that's a tiny 20 KB of images).
Related
I am writing a "waterfall" diagram for an SDR receiver which is displayed in a canvas on a web page.
The canvas has a size of w=1000 h=800 pixels.
The top line is delivered every 50ms from a server.
The browser (using javascript) must move all lines down one line and then insert the new line at the top. This gives the look of a waterfall where all pixels are moving from top to bottom.
It is working fine, but the CPU load for the pixel moving is very high, too high for i.e. a raspberry.
What I am doing is:
var imagedata = context.getImageData(0,0,pixwidth,height-1);
var dataCopy = new Uint8ClampedArray(imagedata.data);
for(i=(dataCopy.length - (2*pixwidth*4)); i>= 0; i--) {
dataCopy[i+ pixwidth*4] = dataCopy[i];
}
imagedata.data.set(dataCopy);
// insert new top line
// ....
context.putImageData(imagedata, 0, 0);
I also tried to directly copy the pixel data in imagedata[some index],
which gives almost the same bad performance.
In another C-Program I did the same thing with a simple memcpy operation which is very fast. But what to do in Javascript ?
There are 800.000 pixels, which are 3.200.000 bytes. How can I copy or move them with the best possible performance in Javascript ?
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
function draw() {
ctx.fillStyle = `hsla(${360 * Math.random()}, 100%, 50%, 1)`;
ctx.fillRect(0, 0, cv.width, 10);
ctx.drawImage(cv, 0, 10);
}
setInterval(function() { draw() }, 200)
<canvas id="cv" width="800" height="400"></canvas>
After drawing a line, take a snapshot of the entire canvas and redraw it with an offset of 10 px on the y scale. Repeat the process and you will get an waterfall like effect.
I'm having a problem drawing sprites on canvas for a school project. My code:
treeImage = new Image();
treeImage.src = "sprites/treeSprites.png";
function rocks() { //to create the rock
this.x = 1920 * Math.random(); //random location on the width of the field
this.y = ground[Math.round(this.x/3)]; //ground is an array that stores the height of the ground
this.draw = function() {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(Math.tan((ground[Math.floor(this.x/3)]-ground[Math.floor(this.x/3)+1])/-3));
//^rotating based on its position on the ground^
ctx.drawImage(treeImage, 200, 50, 50, 50, -25, -50, 50, 50);
ctx.restore();
}
}
len = rockArray.length; //every frame
for (var i = 0;i<len;i++) {
rockArray[i].draw();
}
I only request 50×50px from the image. Exactly outside of the 50×50 there are black lines (which shouldn't interfere because I only request the square within the black lines) but when I draw the rock, the black outlines are visible. (For other reasons, I can't remove the black lines.)
I'm guessing the image JavaScript stores when I load the image is made blurry, and then when I request that part from the image, the lines around are visible too, as the blur "spreads" the lines into the square I request.
Is there a way I can prevent this?
Use ctx.imageSmoothingEnabled = false.
This will make the image sharp instead of smoothed (blurry).
(documentation)
If you draw a vertical line at x=5 and width = 1, the canvas actually draws the line from 4.5 to 5.5 this results in aliasing and a fuzzy line. A quick way to remedy that so it is a solid line is to offset the entire canvas by half a pixel before doing anthing else.
ctx.translate(-0.5, -0.5);
(documentation)
I'm creating an application to calculate how much solar panels would fit on a specific roof.
Users can input the dimensions of their roof.
We only have on size of solar panels available.
I thought a canvas was the way to go but I don't seem to find the information I need..
Requirements
1) Based on the input of the user the canvas should be resized (currently I have a rectangle inside the canvas changing to this size)
2) User should be able to create (and size) objects to put on the roof (chimney, window,..)
3) Based on the open space left solar panels (rectangles) should be automaticly drawn on the canvas
Dimensions and limitations
1px = 2cm
Spacing to edge of roof and object is 7px (14cm)
Solar panel is 169 cm height and 102 cm width
I've checked out the fabric.js library but can't seem to find something close to what I need.
The js I got so far to draw the canvas:
var canvas=document.getElementById("c");
var ctx=canvas.getContext("2d");
var width=50;
var height=35;
var $width=document.getElementById('width');
var $height=document.getElementById('height');
var paneelWidth=101;
var peneelHeight=170;
$width.value=width;
$height.value=height;
draw();
$width.addEventListener("keyup", function(){
width=this.value/2;
draw();
}, false);
$height.addEventListener("keyup", function(){
height=this.value/2;
draw();
}, false);
function draw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillRect(10,10,width,height)
}
Update
The canvas now does resize in a dynamic way based on user input.
I also found the function createPattern(), which is bringing me closer to the solution.
I've added this code to generate a pattern of solar panels in the canvas:
function placepanels(direction) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
var img = document.getElementById("paneel");
var pat = ctx.createPattern(img, direction);
var w2 = canvas.width - 7;
var h2 = canvas.height - 7;
ctx.rect(7, 7, w2, h2);
ctx.fillStyle = pat;
ctx.fill();
}
The -7 on width and height is beacause I need 14cm space on each size of the canvas. Hence why I offset the rectangle containing the pattern 7px from left and top. Currently not able to achieve this on right and bottom side.
Current issue
The result I'm getting is not looking correct, it seems like the pattern repeats wrong (to much repeats) or it's not getting the proper size of the image to repeat.
Updated fiddle: https://jsfiddle.net/8e05ghqy/3/
As for the canvas resize, this function would do it:
changeCanvasSize = function( width, height ) {
$('canvas').width(width)
$('canvas').height(height)
}
Example of usage: changeCanvasSize(450,250) would change the canvas size to 450px of width and 250px of height.
I am just resizing the HTML <canvas> element .width( value ) and .height( value ) works for any HTML element.
I'm trying to do a responsive canvas. All my tests has been doing with a 600x600 canvas and with that height and width it works OK and paint every line correctly. The problem is that I have tried this:
#myCanvas {
background-color: #ffffff;
border:1px solid #000000;
min-height: 600px;
height: 100%;
width: 100%;
}
Just for the record, myCanvas is inside a sm-col-8.
And it looks nice on my laptop and looks nice on my phone but (because of my draw() function, because it was thinking for a square) the draw starts more like in the down-left corner (nearby) and it should start at up-right corner.
So, I don't want to change my draw() function but what I'm looking for is to reescale the canvas size. I mean: If I'm in a laptop/tablet.. with 600x600, show it at that size, but if I'm on my phone which has 384x640 show it like 300x300? I don't know if it could be a good solution.
My draw function:
function drawLines(lines,i,table,xtotal,ytotal){
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var xIni;
var xFin;
var yIni;
var yFin;
xIni = (c.width*parseInt(lines[i][1])/xtotal);
yIni = (c.height*parseInt(lines[i][2])/ytotal);
xFin = (c.width*parseInt(lines[i][3])/xtotal);
yFin = (c.height*parseInt(lines[i][4])/ytotal);
ctx.beginPath();
ctx.moveTo(xIni,c.height-yIni);
ctx.lineTo(xFin,c.height-yFin);
ctx.lineWidth=4;
ctx.strokeStyle = colorAleatorio();
ctx.stroke();
}
With Bootstrap, use:
<canvas id="canvas" class='img-responsive' style="border: 1px solid black;"></canvas>
You can make your html Canvas responsive by using the context.scale command.
The .scale command will scale the internal coordinates system used by canvas.
This means you do not need to change any of your own drawing coordinates because canvas will automatically transform your coordinates into scaled canvas coordinates for you.
// save the original width,height used in drawLines()
var origWidth=600;
var origHeight=600;
var scale=1.00;
// reference to canvas and context
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// call this after resizing
// send in the new maximum width,height desired
function resizeAndRedraw(newMaxWidth,newMaxHeight){
// calc the global scaling factor that fits into the new size
// and also maintains the original aspect ratio
scale=Math.min((newMaxWidth/origWidth),(newMaxHeight/origHeight))
// resize the canvas while maintaining correct aspect ratio
canvas.width=origWidth*scale;
canvas.height=origHeight*scale;
// Note: changing the canvas element's width or height will
// erase the canvas so you must reissue all your drawing commands
drawLines(lines,i,table,xtotal,ytotal);
}
// call drawLines
function drawLines(lines,i,table,xtotal,ytotal){
// scale the canvas coordinate system to the current scale
// Note: This scales the coordinates used internally
// by canvas. It does not resize the canvas element
ctx.scale(s,s);
// now do your drawing commands
// You do not need to adjust your drawing coordinates because
// the Canvas will do that for you
var xIni;
var xFin;
var yIni;
var yFin;
xIni = (c.width*parseInt(lines[i][1])/xtotal);
yIni = (c.height*parseInt(lines[i][2])/ytotal);
xFin = (c.width*parseInt(lines[i][3])/xtotal);
yFin = (c.height*parseInt(lines[i][4])/ytotal);
ctx.beginPath();
ctx.moveTo(xIni,c.height-yIni);
ctx.lineTo(xFin,c.height-yFin);
ctx.lineWidth=4;
ctx.strokeStyle = colorAleatorio();
ctx.stroke();
// restore the context to it's unscaled state
ctx.scale(-s,-s);
}
Can I scaling images in js/canvas using custom algorithm, like binary subdivison or another?
Yes...You can use html5 canvas to do custom image subdivision.
If you are doing simple binary subdivision, you can use the extended version of context.drawImage to clip each tile from the main canvas and scale any subdivision as desired. See the example below.
Non-binary subdivisions are a bit tricky...
Your custom algorithm must generate the vertices or curve control points of each individual path of the desired subdivision.
The technique is to repeat this process for every element of the subdivision:
Create a temporary canvas: var tempCanvas=document.createElement('canvas');
Scale the canvas using tempContext.scale
Define a subpath on the image using context path commands: tempContext.beginPath ...
Restrict future drawing into that path using tempContext.clip
Draw the image onto the canvas using tempContext.drawImage. Since a clipping region has been defined, the image will only be drawn inside the path you have defined.
Use this temporary canvas as an image source to draw the subdivision on your main canvas: mainContext.drawImage(tempCanvas,x,y)
Here's example code and a Demo of simple binary subdivision with scaling:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var subdivisions=[];
var subdivisionIndex=0;
subdivisions.push({
x:0,y:0,
width:174/2,height:110/2,
scale:1.00,maxScale:2.00,scaleDirection:1
});
subdivisions.push({
x:174/2,y:0,
width:174/2,height:110/2,
scale:1.00,maxScale:2.00,scaleDirection:1
});
subdivisions.push({
x:0,y:110/2,
width:174/2,height:110/2,
scale:1.00,maxScale:2.00,scaleDirection:1
});
subdivisions.push({
x:174/2,y:110/2,
width:174/2,height:110/2,
scale:1.00,maxScale:2.00,scaleDirection:1
});
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/cars.jpg";
function start(){
animate();
}
function draw(s){
var x=s.x;
var y=s.y;
var w=s.width;
var h=s.height;
var scaledW=w*s.scale;
var scaledH=h*s.scale;
ctx.clearRect(0,0,cw,ch);
ctx.drawImage(img,0,0);
ctx.drawImage(
img, // clip from img
x,y,w,h, // clip a subdivision
x,y,scaledW,scaledH // draw the subdivision scaled
)
}
function animate(){
requestAnimationFrame(animate);
var s=subdivisions[subdivisionIndex];
draw(s);
if(s.scaleDirection>0){
s.scale*=1.02;
if(s.scale>s.maxScale){s.scaleDirection=-1;}
}else{
s.scale/=1.02;
if(s.scale<1.00){
s.scale=1.00;
s.scaleDirection=1;
subdivisionIndex++;
if(subdivisionIndex>subdivisions.length-1){
subdivisionIndex=0;
}
}
}
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<h4>Scaling subdivisions of a binary subdivided image</h4>
<canvas id="canvas" width=300 height=300></canvas>