Messing with strokeRect, the width seems to be sharing the overall width so a rect with a line thickness of 100 and a width of 200 would really be 300 width including the line. Is this correct or am I missing something else.
Yes, the lineWidth will add 50% of its width to each of the sides of the path itself.
var ctx = document.querySelector("canvas").getContext("2d");
ctx.lineWidth = 100;
ctx.strokeRect(50, 50, 200, 200);
ctx.lineWidth = 1;
ctx.strokeStyle = "red";
ctx.strokeRect(50, 50, 200, 200);
<canvas width=400 height=400></canvas>
Related
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>
Goal: I'm seeking to add an image to a FabricJS group or rectangle object, and for the image to maintain it's original aspect ratio and center fit it's parent width/height.
I don't want the image overflow to show, but will show the overflow as a less opaque green in the examples below:
Landscape Image Example:
If it was a landscape oriented image, it would scale to max height, but then center itself with regards to width placement:
Since I'm not looking for the image overflow to show, the final product should look like this:
Portrait Image Example:
Similarly, if the image was portrait oriented, the image would scale 100% in width, but center itself in height:
And then negating the overflow, this is what I'm looking for as a final product:
Here's my stackblitz so far: https://stackblitz.com/edit/angular-gpfkkw
With the base-case code as follows:
this.canvas = new fabric.Canvas('canvas');
var rect = new fabric.Rect({
left: 100,
top: 50,
width: 450,
height: 200,
fill: '#e3e3e3',
});
var rectGroup = new fabric.Group([rect], {
name: 'Rectangle',
});
this.canvas.add(rectGroup);
fabric.Image.fromURL('https://placehold.it/888x500&text=16:9', (img) => {
let bounds = rectGroup.getBoundingRect();
const scaleFactor = Math.min(
Math.min(bounds.width / img.width),
Math.min(bounds.height / img.height)
);
img.scale(scaleFactor);
img.set({
top: bounds.top + Math.max(bounds.height - img.height * scaleFactor, 0)/2,
left: bounds.left + Math.max(bounds.width - img.width * scaleFactor, 0)/2,
});
rectGroup.addWithUpdate(img);
this.canvas.renderAll();
}
This is obviously not the right solution, but a start. Any advice?
I figured it out, here's the Stackblitz: https://stackblitz.com/edit/angular-yoo8h5
First, I compared whether or not the rectangle aspect ratio is proportional to the image through an if/else statement which decides whether or not to initially scale the image to the rectangle width/height.
Then I set either the top or the left attribute of the image to the rectangle boundary point. Then calculating the center point of the rectangle and subtracting half of the image's boundary and lastly, setting the rectangle group object's clipPath to that of the rectangle, it worked!
fabric.Image.fromURL('https://placehold.it/888x500&text=16:9', (img) => {
let bounds = rectGroup.getBoundingRect();
if ((bounds.height / bounds.width) >= (img.height / img.width)) {
img.scaleToHeight(bounds.height);
img.set({
top: bounds.top,
left: (bounds.left + (bounds.width/2)) - (img.getBoundingRect().width/2)
});
}
else {
img.scaleToWidth(bounds.width);
img.set({
top: (bounds.top + (bounds.height/2)) - (img.getBoundingRect().height/2),
left: bounds.left
});
}
rectGroup.addWithUpdate(img);
rectGroup.clipPath = rect;
this.canvas.renderAll();
});
A bit late here but chipping in in case this might be helpful.
You were heading in the right direction with Math.min, but you can use Math.max instead for the side with the larger group/image ratio to scale to the full length of the side. You can then set the x y origins to center.
fabric.Image.fromURL('https://placehold.it/888x500&text=16:9', (img) => {
let bounds = rectGroup.getBoundingRect();
const scaleFactor = Math.max(bounds.width / img.width, bounds.height / img.height);
img.set({
originX: 'center',
originY: 'center',
scaleX: scaleFactor,
scaleY: scaleFactor
});
rectGroup.addWithUpdate(img);
this.canvas.renderAll();
}
I need to rotate the image 180 degrees, but when I add in the rotate code, the image becomes a square.
var moulding_matte_canvas_height = [],
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d"),
width = 5.171363636363637,
opening_i = 0,
rotate = 180 * Math.PI / 180,
i = 2;
moulding_matte_canvas_height[0] = 225;
canvas.width = 285;
canvas.height = 335;
img = new Image();
img.src = 'http://www.asa.tframes.org:1881/system/components/compimg/80ac89fad42cf14561b641df241bf406/pattern';
function moulding_get_segment_width(img_width, opening_i)
{
return 175;
}
//
$(img).on("load", function() {
ctx.save();
ctx.translate(width, moulding_matte_canvas_height[opening_i]);
//ctx.rotate(rotate);
//ctx.rotate(Math.PI);
ctx.drawImage(img, 0, 0, moulding_get_segment_width(img.width, i), width);
ctx.restore();
//
});
$("#mattes").append(canvas); /*appending the canvas*/
http://fiddle.jshell.net/onpa628e/1/
(Whitespace is intentional due to other elements that will be drawn and the red next to the image is just for clarity)
Without rotate:
With rotate:
Expected:
Your image looks like a square, because it's only showing a tiny portion on the canvas. This happens because translate is only moving ~ 5px (your "width" variable) on the x axis.
Here's your example modified with a higher x value.
I'm trying to figure out how to get my legend to appear at the bottom of a chart but having no luck.
In the code below, since my chart has a height of 600, i figure i could simply add an offset of 600 for the Y coordinate and that would do the trick. Does not work however.
var svg = dimple.newSvg("#chart", 800, 600);
...
chart.addLegend(0, 600 + 40, 200, 10, "left");
Anyone know how to solve this either with dimple.js or d3.js? It would also be great if i did not have to hard code these values and instead could do something like this:
chart.addLegend(0, d3.select("#chart").height + 40, 200, 10, "left");
here is the documentation for addLegend for dimple.js:
https://github.com/PMSI-AlignAlytics/dimple/wiki/dimple.chart#addLegend
Your code should be Ok.
Please check js fiddle(http://jsfiddle.net/ch2187dd/)
var svg = dimple.newSvg("#chartContainer", 590, 540);
var myChart = new dimple.chart(svg, data);
myChart.setBounds(90, 35, 480, 325)
myChart.addCategoryAxis("x", ["date"]);
myChart.addCategoryAxis("y", "close");
myChart.addSeries("Price Tier", dimple.plot.bubble);
myChart.addLegend(90, 480, 330, 20, "left");
myChart.draw();
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);