Good day.
I am looking for abit of advice. Before judging, please take note I am new to coding, with only a few weeks of experience.
I am trying to make the cloud shape I have draw onto the canvas, animated, using JavaScript. (Please see link below to the type of animation I am looking to achieve.) I have searched everywhere for how to achieve this, but it seems all the links either deal with an actual image, or a rectangle, and not a custom drawn shape. So my question is this, is it possible to do such an animation on a custom shape, and, if not, what is the best way to go about it to achieve said desired effect.
<!DOCTYPE HTML>
<HTML>
<head>
<style>
body {
margin: 100px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="Canvas" width="700" height="600" style="border:1px solid #005BAB;"></canvas>
<script>
var canvas = document.getElementById('Canvas');
var context = canvas.getContext('2d');
// gradients
var grd = context.createRadialGradient(550, 150, 25, 500, 200, 400);
grd.addColorStop (0, '#0586f8');
grd.addColorStop (1, '#015baa');
// blue block shadow
context.shadowColor = '#999';
context.shadowBlur = 35;
context.shadowOffsetX = 8;
context.shadowOffsetY = 8;
context.globalCompositeOperation = 'source-over';
context.beginPath();
context.fill();
// blue block
context.beginPath();
context.moveTo(50, 35);
context.lineTo(50, 525);
context.lineTo(550, 525);
context.lineTo(550, 35);
context.closePath();
context.lineWidth = 0;
context.strokeStyle = '#999';
context.fillStyle = grd;
context.fill();
// cloud shadow
context.shadowColor = '#232323';
context.shadowBlur = 20;
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.beginPath();
context.globalAlpha = 0.9;
context.fill();
// cloud drawing
context.beginPath();
context.moveTo(172, 180);
context.bezierCurveTo(145, 180, 120, 234, 185, 240);
context.lineTo(375, 240);
context.bezierCurveTo(495, 234, 405, 110, 382, 168);
context.bezierCurveTo(440, 85, 280, 60, 325, 120);
context.bezierCurveTo(320, 42, 190, 60, 200, 120);
context.bezierCurveTo(120, 100, 140, 200, 170, 180);
context.lineWidth = 1;
context.fillStyle = 'white';
context.fill();
context.strokeStyle();
context.lineCap = 'round';
context.lineJoin = 'round';
context.stroke();
</script>
</body>
</HTML>
http://www.html5canvastutorials.com/advanced/html5-canvas-oscillation-animation/
You could create another canvas with javascript:
var cloud = document.createElement('canvas');
cloud.width = <width of the cloud>;
cloud.height = <height of the cloud>;
Then draw your cloud onto that canvas. You should adjust your coordinates so that the top left of the cloud's bounding box is at point (0, 0). This way the cloud canvas is only as big as it needs to be.
Then you can treat your cloud just like you would an image.
context.drawImage(cloud, 0, 0);
Related
The following code draws two rectangles:
const demoCanvas = document.getElementById('canvas-demo').getContext('2d');
window.onload = function() {
demoCanvas.fillStyle = 'red';
demoCanvas.rect(10, 10, 60, 60);
demoCanvas.fill(); // first rectangle
demoCanvas.fillStyle = 'blue';
demoCanvas.rect(10, 110, 60, 60);
demoCanvas.fill();
}
<canvas id="canvas-demo" width="300" height="300">
The output is two blue rectangles. I tried adding begin and close path as well, but the rectangles take only one color for some reason. In this case, it's blue. How can I fix this?
Here is the solution:
const demoCanvas = document.getElementById('canvas-demo').getContext('2d');
window.onload = function() {
demoCanvas.beginPath();
demoCanvas.fillStyle = 'red';
demoCanvas.rect(10, 10, 60, 60);
demoCanvas.fill();
demoCanvas.closePath();
demoCanvas.beginPath();
demoCanvas.fillStyle = 'blue';
demoCanvas.rect(10, 110, 60, 60);
demoCanvas.fill();
demoCanvas.closePath();
}
<canvas id="canvas-demo" width="300" height="300">
Hope you found this helpful, Cheers 🙂.
When panning in the first canvas, things work like expected. When panning in the second canvas, it doesn't work like expected. I expected both to work the same. The second globe spins rapidly after a little bit of panning, the first globe keeps the cursor on the same coordinates.
https://codepen.io/tonytrupe/pen/jOqjGvE
class UI {
constructor(canvas) {
var width = canvas.width,
height = canvas.height;
//set projection type here, geoOrthographic, geoWinkel3
var projection = d3
.geoWinkel3()
//.scale((Math.min(width, height)) / 2)
.translate([width / 2, height / 2])
//.rotate([0,0,0])
.fitExtent(
[
[6, 6],
[width - 6, height - 6]
],
{
type: "Sphere"
}
);
draw();
//this.addZoomPan = function () {
d3
.geoZoom()
.northUp(true)
.projection(projection)
.onMove(draw)(canvas);
//};
function draw() {
var ctx = canvas.getContext("2d");
var path = d3.geoPath().context(ctx).projection(projection);
// Store the current transformation matrix
ctx.save();
// Use the identity matrix while clearing the canvas
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Restore the transform
ctx.restore();
var border = {
type: "Sphere"
};
ctx.beginPath();
path(border);
ctx.strokeStyle = "#000";
ctx.stroke();
var lat = 45;
var lon = 45;
var graticule = d3.geoGraticule().step([lat, lon]);
ctx.beginPath();
path(graticule());
ctx.strokeStyle = "#000";
ctx.stroke();
}
}
}
//var one = new UI(document.getElementById("one"));
//var two = new UI(document.getElementById("two"));
var three = new UI(document.getElementById("three"));
html
<html>
<script src="//d3js.org/d3.v6.js"></script>
<script src="//d3js.org/d3-geo.v2.min.js"></script>
<script src="//d3js.org/d3-geo-projection.v3.min.js"></script>
<script src="//unpkg.com/d3-geo-zoom"></script>
<!--removing all but the last canvas element makes things work as expected-->
<canvas id="one" class="canvas" width="320" height="200"></canvas>
<canvas id="two" class="canvas" width="320" height="200"></canvas>
<canvas id="three" class="canvas" width="320" height="200"></canvas>
</html>
https://github.com/vasturiano/d3-geo-zoom/issues/12
It previously wasn't getting pointer location relative to the node element. Now it is.
const pointers = d3Pointers(zoomEv, nodeEl);
https://github.com/vasturiano/d3-geo-zoom/blob/86da0d98f267a838a4715abec60e4a278ace2121/src/geoZoom.js#L59
I would like to clip some round or rectangular holes of a given path.
CanvasRenderingContext2D.clearRect() doesn't work here because I need to reveal the background.
I referenced the answer here:
https://stackoverflow.com/a/18993901/3066086
but it doesn't work when the shapes are touched.
Here is the code to demonstrate my application and picture of the result/desired result:
<canvas id="canvas" width = "500" height="500" ></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.rect(0, 0,500, 500);
ctx.fillStyle = 'rgba(100,100,100,1)';
ctx.fill();
ctx.beginPath();
ctx.rect(0,0,500,500);
ctx.arc(100, 250, 50, 0, 2 * Math.PI);
ctx.closePath();
ctx.arc(300, 250, 50, 0, 2 * Math.PI);
ctx.closePath();
ctx.rect(95,245,200,10);
ctx.clip('evenodd');
ctx.beginPath();
ctx.rect(5, 5, 400, 400);
ctx.fillStyle = 'rgba(255,0,0,1)';
ctx.fill();
</script>
result:
desired result:
Using globalCompositeOperation
You can use it to cut out parts of the image ctx.globalCompositeOperation = "destination-out";
And to draw only to transparent parts of the image. ctx.globalCompositeOperation = "destination-over"
So rather than draw the background first, you draw the outer shape, then cut out what you need and then finaly draw the background behind everything.
Example
const ctx = canvas.getContext('2d');
// draw red box first
ctx.beginPath();
ctx.rect(5, 5, 400, 400)
ctx.fillStyle = "#F00";
ctx.fill();
// Cut out circles using globalCompositeOperation "destination-out"
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#000";
ctx.beginPath();
ctx.arc(100, 250, 50, 0, 2 * Math.PI);
ctx.closePath();
ctx.arc(300, 250, 50, 0, 2 * Math.PI);
ctx.closePath();
ctx.rect(95,245,200,10);
ctx.fill();
// Add background last using "destination-over";
ctx.globalCompositeOperation = "destination-over";
ctx.rect(0, 0,500, 500);
ctx.fillStyle = "#999";
ctx.fill();
<canvas id="canvas" width = "500" height="500" style="width:200px;height:200px;"></canvas>
Two canvases one on top of the other as below. The second canvas cvs should be transparent since by default all canvases are transparent and I do a clearRect just to make sure.
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<canvas id="myCanvas" width="578" height="430"></canvas>
<canvas id="cvscan" style="display:none"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var cvs = document.getElementById('cvscan');
var cvsctx = cvs.getContext('2d');
context.save();
context.clearRect(0, 0, 578, 430);
context.fillStyle = 'gray';
context.fillRect(0, 0, 578, 430);
context.beginPath();
context.moveTo(0, 0);
context.lineWidth = 3;
context.lineTo(578, 430);
context.strokeStyle = '#444444';
context.stroke();
context.moveTo(578, 0);
context.lineTo(0, 430);
context.stroke();
cvs.width = 200;
cvs.height = 200;
cvsctx.clearRect(0, 0, cvscan.width, cvscan.height);
var curimg = cvsctx.getImageData(0, 0, cvs.width, cvs.height);
context.putImageData(curimg, 50, 50);
context.restore();
</script>
</body>
</html>
I believe the second canvas should be transparent and (since there's nothing drawn in it) invisible -- but it's filled with white. What am I missing?
UPDATE this is what I see -- I believe the white box should not be there.
Here is the image
The canvas with id="cvscan" is being cleared, which means the image data is being set to 0 for all, rgba(0,0,0,0), values.
Next, that image data is being painted onto the canvas with id="myCanvas", updating the pixel on "myCanvas" to zero.
So, the canvas ("myCanvas") is now transparent at the painted location, and is seen as white, because the background of the body is now showing.
To validate the results a test was run logging the data of the image, and then updating bgcolor to red.
(see photos)
I have a 3D scatter plot that I've constructed using Three.js. I would like to add an HTML5 canvas drawing to the scene, such that the drawing appears on a certain position of the page.
To better explain, here is a fiddle of the scatter plot:
http://jsfiddle.net/jmg157/ynFzw/6/
And let's say I wanted to add the below drawing to the bottom left (or right) of the page (which will eventually be a legend for the plot):
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.beginPath();
context.rect(3, 3, 300, 200);
context.fillStyle = 'white';
context.fill();
context.lineWidth = 3;
context.strokeStyle = 'black';
context.stroke();
context.font = "20px Verdana";
context.fillStyle = "#000";
context.fillText("Legend", 120, 25);
Thank you for any help/suggestions on how to go about doing this.
Here is a pattern you can follow:
var canvas = document.createElement( 'canvas' );
var context = canvas.getContext('2d');
context.beginPath();
context.rect(3, 3, 150, 100);
context.fillStyle = 'white';
context.fill();
context.lineWidth = 3;
context.strokeStyle = 'black';
context.stroke();
context.font = "20px Verdana";
context.fillStyle = "#000";
context.fillText("Legend", 40, 25);
canvas.style.position = 'absolute';
canvas.style.top = ( window.innerHeight - 100 - 10 ) + 'px';
canvas.style.left = '10px';
canvas.style.margin = '0px';
canvas.style.padding = '0px';
document.body.appendChild( canvas );
Your fiddle updated: http://jsfiddle.net/ynFzw/7/