radial gradient color effect on concentric circles - geometry

How to apply gradient colors on 5 concentric circles using html5 canvas
gradient = context.createRadialGradient(startx, starty,radAvg, xEnd, yEnd,radAvg);
gradient.addColorStop(0, startColor);
gradient.addColorStop(1.0, endColor);
Current results:
JavaScript code:
var colors = ["#B8D430", "#3AB745", "#029990", "#3501CB",
"#2E2C75", "#673A7E", "#CC0071", "#F80120",
"#F35B20", "#FB9A00", "#FFCC00", "#FEF200"];
var restaraunts = ["Wendy's", "McDonalds", "Chick-fil-a", "Five Guys",
"Gold Star", "La Mexicana", "Chipotle", "Tazza Mia",
"Panera", "Just Crepes", "Arby's", "Indian"];
var startAngle = 0;
var arc = Math.PI / 6;
var spinTimeout = null;
var spinArcStart = 10;
var spinTime = 0;
var spinTimeTotal = 0;
var ctx;
function draw() {
drawRouletteWheel();
}
function drawRouletteWheel() {
var canvas = document.getElementById("wheelcanvas");
if (canvas.getContext) {
var outsideRadius = 200;
var textRadius = 160;
var insideRadius = 125;
ctx = canvas.getContext("2d");
ctx.clearRect(0,0,500,500);
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.font = 'bold 12px sans-serif';
for(var i = 0; i < 12; i++) {
var angle = startAngle + i * arc;
ctx.fillStyle = colors[i];
ctx.beginPath();
ctx.arc(250, 250, outsideRadius, angle, angle + arc, false);
ctx.arc(250, 250, insideRadius, angle + arc, angle, true);
ctx.stroke();
ctx.fill();
ctx.save();
ctx.shadowOffsetX = -1;
ctx.shadowOffsetY = -1;
ctx.shadowBlur = 0;
ctx.shadowColor = "rgb(220,220,220)";
// ctx.fillStyle = "black";
ctx.translate(250 + Math.cos(angle + arc / 2) * textRadius, 250 + Math.sin(angle + arc / 2) * textRadius);
ctx.rotate(angle + arc / 2 + Math.PI / 2);
var text = restaraunts[i];
ctx.fillText(text, -ctx.measureText(text).width / 2, 0);
ctx.restore();
}
//Arrow
ctx.fillStyle = "black";
ctx.beginPath();
ctx.moveTo(250 - 4, 250 - (outsideRadius + 5));
ctx.lineTo(250 + 4, 250 - (outsideRadius + 5));
ctx.lineTo(250 + 4, 250 - (outsideRadius - 5));
ctx.lineTo(250 + 9, 250 - (outsideRadius - 5));
ctx.lineTo(250 + 0, 250 - (outsideRadius - 13));
ctx.lineTo(250 - 9, 250 - (outsideRadius - 5));
ctx.lineTo(250 - 4, 250 - (outsideRadius - 5));
ctx.lineTo(250 - 4, 250 - (outsideRadius + 5));
ctx.fill();
}
}
function spin() {
spinAngleStart = Math.random() * 10 + 10;
spinTime = 0;
spinTimeTotal = Math.random() * 3 + 4 * 1000;
rotateWheel();
}
function rotateWheel() {
spinTime += 30;
if(spinTime >= spinTimeTotal) {
stopRotateWheel();
return;
}
var spinAngle = spinAngleStart - easeOut(spinTime, 0, spinAngleStart, spinTimeTotal);
startAngle += (spinAngle * Math.PI / 180);
drawRouletteWheel();
spinTimeout = setTimeout('rotateWheel()', 30);
}
function stopRotateWheel() {
clearTimeout(spinTimeout);
var degrees = startAngle * 180 / Math.PI + 90;
var arcd = arc * 180 / Math.PI;
var index = Math.floor((360 - degrees % 360) / arcd);
ctx.save();
ctx.font = 'bold 30px sans-serif';
var text = restaraunts[index]
ctx.fillText(text, 250 - ctx.measureText(text).width / 2, 250 + 10);
ctx.restore();
}
function easeOut(t, b, c, d) {
var ts = (t/=d)*t;
var tc = ts*t;
return b+c*(tc + -3*ts + 3*t);
}
draw();

here is my solution
var Mid_diff=(startAngle+endAngle)/2;
var dest_x=arc.center.x+(arc.maxRadius*Math.cos(Mid_diff));
var dest_y=arc.center.y+(arc.maxRadius*Math.sin(Mid_diff));
var gradient = context.createRadialGradient( arc.center.x,arc.center.y,0,dest_x,dest_y,arc.maxRadius);
gradient.addColoStop(0,rgba(0,12,107,0);
gradient.addColoStop(1,rgba(0,12,107,1);
context.strokeStyle = gradient;
worked

Related

Plotting a discrete-time signal shows amplitude modulation

I'm trying to render a simple discrete-time signal using a canvas element. However, the representation seems to be inaccurate. As you can see in the code snippet the signal appears to be amplitude modulated after the frequency reaches a certain threshold. Even though it's well below the Nyquist limit of <50Hz (assuming a sampling rate of 100Hz in this example).
For very low frequencies like 5Hz it looks perfectly fine.
How would I go about rendering this properly? And does it work for more complex signals (say, the waveform of a song)?
window.addEventListener('load', () => {
const canvas = document.querySelector('canvas');
const frequencyElem = document.querySelector('#frequency');
const ctx = canvas.getContext('2d');
const renderFn = t => {
const signal = new Array(100);
const sineOfT = Math.sin(t / 1000 / 8 * Math.PI * 2) * 0.5 + 0.5;
const frequency = sineOfT * 20 + 3;
for (let i = 0; i < signal.length; i++) {
signal[i] = Math.sin(i / signal.length * Math.PI * 2 * frequency);
}
frequencyElem.innerText = `${frequency.toFixed(3)}Hz`
render(ctx, signal);
requestAnimationFrame(renderFn);
};
requestAnimationFrame(renderFn);
});
function render(ctx, signal) {
const w = ctx.canvas.width;
const h = ctx.canvas.height;
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = 'red';
ctx.beginPath();
signal.forEach((value, i) => {
const x = i / (signal.length - 1) * w;
const y = h - (value + 1) / 2 * h;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
}
#media (prefers-color-scheme: dark) {
body {
background-color: #333;
color: #f6f6f6;
}
}
<canvas></canvas>
<br/>
Frequency: <span id="frequency"></span>
It looks right to me. At higher frequencies, when the peak falls between two samples, the sampled points can be a lot lower than the peak.
If the signal only has frequencies < Nyquist, then the signal can be reconstructed from its samples. That doesn't mean that the samples look like the signal.
As long as your signal is oversampled by 2x or more (or so), you can draw it pretty accurately by using cubic interpolation between the sample points. See, for example, Catmull-Rom interpolation in here: https://en.wikipedia.org/wiki/Cubic_Hermite_spline
You can use the bezierCurveTo method in HTML Canvas to draw these interpolated curves. If you need to use lines, then you should find any maximum or minimum points that occur between samples and include those in your path.
I've edited your snippet to use the bezierCurveTo method with Catmull-Rom interpolation below:
window.addEventListener('load', () => {
const canvas = document.querySelector('canvas');
const frequencyElem = document.querySelector('#frequency');
const ctx = canvas.getContext('2d');
const renderFn = t => {
const signal = new Array(100);
const sineOfT = Math.sin(t / 1000 / 8 * Math.PI * 2) * 0.5 + 0.5;
const frequency = sineOfT * 20 + 3;
for (let i = 0; i < signal.length; i++) {
signal[i] = Math.sin(i / signal.length * Math.PI * 2 * frequency);
}
frequencyElem.innerText = `${frequency.toFixed(3)}Hz`
render(ctx, signal);
requestAnimationFrame(renderFn);
};
requestAnimationFrame(renderFn);
});
function render(ctx, signal) {
const w = ctx.canvas.width;
const h = ctx.canvas.height;
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = 'red';
ctx.beginPath();
const dx = w/(signal.length - 1);
const dy = -(h-2)/2.0;
const c = 1.0/2.0;
for (let i=0; i < signal.length-1; ++i) {
const x0 = i * dx;
const y0 = h*0.5 + signal[i]*dy;
const x3 = x0 + dx;
const y3 = h*0.5 + signal[i+1]*dy;
let x1,y1,x2,y2;
if (i>0) {
x1 = x0 + dx*c;
y1 = y0 + (signal[i+1] - signal[i-1])*dy*c/2;
} else {
x1 = x0;
y1 = y0;
ctx.moveTo(x0, y0);
}
if (i < signal.length-2) {
x2 = x3 - dx*c;
y2 = y3 - (signal[i+2] - signal[i])*dy*c/2;
} else {
x2 = x3;
y2 = y3;
}
ctx.bezierCurveTo(x1,y1,x2,y2,x3,y3);
}
ctx.stroke();
}
#media (prefers-color-scheme: dark) {
body {
background-color: #333;
color: #f6f6f6;
}
}
<canvas></canvas>
<br/>
Frequency: <span id="frequency"></span>

drawing translated paths in wrong coordinates

I am trying to build a simple game for educational purposes and i am having problem with drawing object in correct coordinates.
I had svg spaceship that i wanted to utilise, so i converted it to canvas paths system using this tool SVG to Canvas converter.
Since svg was originally designed on 1000x1000 plane i had to apply scale to reach desired size
// calculate scale, if size (r) has to be 40px then 40px / original size (1000) = 0.04;
var size = r / 1000;
ctx.scale(size, size); // apply desired size
It seemed to work but then when it comes to rendering it in set coordinates (x,y), it clearly is off the mark.
In the demo you can see that ship is rotating and moving outside of where it should be, also disproportion seems to vary depending on location which clearly means something is wrong.
Maybe someone could find the cause and how i can fix it? The aim ofc is for the ship to be rendered in the center of helper box.
This is demo code:
var cvs = document.querySelector('canvas'),
ctx = cvs.getContext('2d'),
w = cvs.width = 1000,
h = cvs.height = 1000,
helper = document.querySelector('.helper');
var ship = function(x, y, r, a) {
var size = r / 1000; // calculate scale, if size (r) has to be 40px then 40px / original size (1000) = 0.04;
x -= r / 2; // go back half of its width to center ship on passed x coord
y -= r / 2; // go back half of its height to center ship on passed y coord
/* draw original ship ----> */
ctx.save();
ctx.translate(x, y);
ctx.scale(size, size); // apply desired size
ctx.rotate((a + 90) * Math.PI / 180); // rotate ship on its center
ctx.translate(-x, -y);
ctx.beginPath();
ctx.moveTo(341.4,856.1);
ctx.lineTo(173.2,881.8000000000001);
ctx.bezierCurveTo(159.79999999999998,883.8000000000001,146.5,877.7,139.39999999999998,866.2);
ctx.lineTo(76.69999999999997,764.2);
ctx.bezierCurveTo(73.19999999999997,758.5,71.59999999999998,752,71.79999999999997,745.6);
ctx.bezierCurveTo(71.79999999999997,745.1,71.69999999999997,744.5,71.69999999999997,744);
ctx.lineTo(71.69999999999997,528.2);
ctx.bezierCurveTo(71.69999999999997,509.6,86.79999999999997,494.50000000000006,105.39999999999998,494.50000000000006);
ctx.bezierCurveTo(123.99999999999997,494.50000000000006,139.09999999999997,509.6000000000001,139.09999999999997,528.2);
ctx.lineTo(139.09999999999997,617.7);
ctx.lineTo(273.79999999999995,377.80000000000007);
ctx.lineTo(341.49999999999994,493.30000000000007);
ctx.lineTo(341.49999999999994,856.1);
ctx.closePath();
ctx.moveTo(894.7,494.5);
ctx.bezierCurveTo(876.1,494.5,861,509.6,861,528.2);
ctx.lineTo(861,617.7);
ctx.lineTo(726.3,377.8);
ctx.lineTo(658.5999999999999,493.3);
ctx.lineTo(658.5999999999999,856.1);
ctx.lineTo(826.8,881.8000000000001);
ctx.bezierCurveTo(840.1999999999999,883.8000000000001,853.5,877.7,860.5999999999999,866.1);
ctx.lineTo(923.3,764.1);
ctx.bezierCurveTo(926.8,758.4,928.4,751.9,928.1999999999999,745.5);
ctx.bezierCurveTo(928.1999999999999,745,928.3,744.4,928.3,743.9);
ctx.lineTo(928.3,528.2);
ctx.bezierCurveTo(928.3,509.6,913.3,494.5,894.7,494.5);
ctx.closePath();
ctx.moveTo(591.2,857.6);
ctx.lineTo(533.7,900.5);
ctx.lineTo(533.7,956.4);
ctx.bezierCurveTo(533.7,975,518.6,990.1,500.00000000000006,990.1);
ctx.bezierCurveTo(481.40000000000003,990.1,466.30000000000007,975,466.30000000000007,956.4);
ctx.lineTo(466.30000000000007,900.5);
ctx.lineTo(408.80000000000007,857.6);
ctx.lineTo(408.80000000000007,484.2);
ctx.bezierCurveTo(408.80000000000007,478.2,407.20000000000005,472.3,404.20000000000005,467.2);
ctx.lineTo(312.00000000000006,309.79999999999995);
ctx.lineTo(470.6,27.2);
ctx.bezierCurveTo(476.6,16.6,487.8,10,500,10);
ctx.bezierCurveTo(512.2,10,523.4,16.6,529.4,27.2);
ctx.lineTo(688.0999999999999,309.8);
ctx.lineTo(595.8999999999999,467.20000000000005);
ctx.bezierCurveTo(592.8999999999999,472.40000000000003,591.2999999999998,478.20000000000005,591.2999999999998,484.20000000000005);
ctx.lineTo(591.2999999999998,857.6);
ctx.closePath();
ctx.moveTo(591,318.2);
ctx.bezierCurveTo(605.3,306.4,607.4,285.09999999999997,595.5,270.8);
ctx.bezierCurveTo(591.6,266.1,555.8,224.60000000000002,500.8,224.60000000000002);
ctx.bezierCurveTo(446.3,224.60000000000002,409.1,265.40000000000003,405,270.1);
ctx.bezierCurveTo(392.7,284.1,394.1,305.5,408.1,317.70000000000005);
ctx.bezierCurveTo(414.5,323.30000000000007,422.40000000000003,326.1,430.3,326.1);
ctx.bezierCurveTo(439.6,326.1,448.90000000000003,322.20000000000005,455.5,314.70000000000005);
ctx.bezierCurveTo(461.2,308.40000000000003,480.4,292.1,500.8,292.1);
ctx.bezierCurveTo(524.7,292.1,543.6,313.8,543.6,313.8);
ctx.bezierCurveTo(555.5,328,576.7,330.1,591,318.2);
ctx.closePath();
ctx.fillStyle = '#000';
ctx.strokeStyle = '#000';
ctx.lineWidth = 1;
ctx.fill();
ctx.stroke();
ctx.restore();
/* <---- draw original ship */
};
var c = { x: 100, y: 100, a: 270, r: 40 };
var rotator = 1;
var render = function() {
ctx.clearRect(0, 0, w, h);
ctx.fillStyle = '#ccc';
ctx.fillRect(0, 0, w, h);
ship(c.x, c.y, c.r, c.a);
c.x += rotator;
c.y += rotator;
c.a += rotator;
if(c.x >= 950 || c.y >= 950 || c.x <= 50 || c.y <= 50) {
rotator *= -1;
}
/* helper debug section ----> */
ctx.save();
ctx.beginPath();
ctx.moveTo(c.x, 0);
ctx.lineTo(c.x, 1000);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, c.y);
ctx.lineTo(1000, c.y);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(c.x, c.y);
ctx.lineTo(c.x, c.y - 20);
ctx.lineTo(c.x - 20, c.y - 20);
ctx.lineTo(c.x - 20, c.y);
ctx.lineTo(c.x - 20, c.y + 20);
ctx.lineTo(c.x, c.y + 20);
ctx.lineTo(c.x + 20, c.y + 20);
ctx.lineTo(c.x + 20, c.y);
ctx.lineTo(c.x + 20, c.y - 20);
ctx.lineTo(c.x, c.y - 20);
ctx.closePath();
ctx.stroke();
ctx.restore();
/* <---- helper debug section */
requestAnimationFrame(render);
};
render();
canvas {
position:absolute;
top:0;
left:0;
}
<canvas></canvas>
A nice thing to know is that you can draw svg paths in canvas using Path2D as I did in the next demo. Also I've recalculated your path. The path you are using is 1000/1000 units. I've recalculated your path to begin at -500 both in x and y, putting the center in the origin: point {x:0,y:0}.
let svgPath =`M-158.60000000000002,356.1L-326.8,381.79999999999995C-340.2,383.79999999999995,-353.5,377.70000000000005,-360.6,366.20000000000005L-423.3,264.20000000000005C-426.8,258.5,-428.4,252,-428.2,245.60000000000002,-428.2,245.10000000000002,-428.3,244.5,-428.3,244L-428.3,28.200000000000045C-428.3,9.600000000000023,-413.2,-5.5,-394.6,-5.5,-376,-5.5,-360.9,9.600000000000023,-360.99,28.200000000000045L-360.9,117.70000000000005,-226.2,-122.19999999999999,-158.5,-6.699999999999989,-158.5,356.1
M394.70000000000005,-5.5C376.1,-5.5,361,9.600000000000023,361,28.200000000000045L361,117.70000000000005,226.29999999999995,-122.19999999999999,158.69000000000005,-6.699999999999989,158.60000000000002,356.1,326.79999999999995,381.79999999999995C340.20000000000005,383.79999999999995,353.5,377.70000000000005,360.6,366.1L423.29999999999995,264.1C426.79999999999995,258.4,428.4,251.89999999999998,428.20000000000005,245.5,428.20000000000005,245,428.29999999999995,244.39999999999998,428.29999999999995,243.89999999999998,428.29999999999995,9.600000000000023,413.29999999999995,-5.5,394.70000000000005,-5.5z
M91.20000000000005,357.6L33.700000000000045,400.5,33.700000000000045,456.4C33.700000000000045,475,18.600000000000023,490.1,0,490.1,-18.600000000000023,490.1,-33.69999999999999,475,-33.69999999999999,456.4L-33.69999999999999,400.5,-91.19999999999999,357.6,-91.13,-15.800000000000011C-91.19999999999999,-21.80000000000001,-92.80000000000001,-27.69999999999999,-95.80000000000001,-32.80000000000001L-188,-190.2,-29.399999999999977,-472.8C-23.399999999999977,-483.4,-12.199999999999989,-490,0,-490,12.200000000000045,-490,23.399999999999977,-483.4,29.399999999999977,-472.8L188.10000000000002,-190.2,95.89999999999998,-32.80000000000001C92.89999999999998,-27.569999999999993,91.29999999999995,-21.80000000000001,91.29999999999995,-15.800000000000011L91.29999999999995,357.6z
M91,-181.8C105.29999999999995,-193.60000000000002,107.39999999999998,-214.89999999999998,95.5,-229.2,91.60000000000002,-233.89999999999998,55.799999999999955,-275.4,0.8000000000000114,-275.4,-53.69999999999999,-275.4,-90.89999999999998,-234.60000000000002,-95,-229.89999999999998,-107.30000000000001,-215.89999999999998,-105.89999999999998,-194.5,-91.89999999999998,-182.3,-85.5,-176.7,-77.60000000000002,-173.89999999999998,-69.69999999999999,-173.89999999999998,-60.39999999999998,-173.89999999999998,-51.10000000000002,-177.8,-44.5,-185.3,-38.80000000000001,-191.60000000000002,-19.600000000000023,-207.89999999999998,0.8000000000000114,-207.89999999999998,24.700000000000045,-207.89999999999998,43.60000000000002,-186.2,43.60000000000002,-186.2,55.5,-172,76.70000000000005,-169.89999999999998,91,-181.8z`;
var cvs = document.querySelector('canvas'),
ctx = cvs.getContext('2d'),
w = cvs.width = window.innerWidth,
h = cvs.height = window.innerHeight;
ctx.fillStyle = "black";
let shuttle = new Path2D(svgPath);
let angle = 0;
let x = 0;
let y = 0;
let increment = 1
let the_scale = .1;
//helper size
//1000 is the size of the svg path
let hs = (1000 * the_scale) / 2;
function frame(){
window.requestAnimationFrame(frame);
ctx.clearRect(0, 0, w,h )
angle += increment/100;
x+=increment;
if(y < h + hs){y+=increment;}else{y = -hs; x=-hs;}
ctx.save();
ctx.fillStyle="#333"
ctx.translate(x,y);
ctx.scale(the_scale,the_scale);
ctx.rotate(angle);
ctx.fill(shuttle);
ctx.restore();
helper(x,y)
}
frame();
function helper(x,y){
ctx.strokeStyle="red"
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(w, y);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x, y - hs);
ctx.lineTo(x - hs, y - hs);
ctx.lineTo(x - hs, y);
ctx.lineTo(x - hs, y + hs);
ctx.lineTo(x, y + hs);
ctx.lineTo(x + hs, y + hs);
ctx.lineTo(x + hs, y);
ctx.lineTo(x + hs, y - hs);
ctx.lineTo(x, y - hs);
ctx.closePath();
ctx.stroke();
}
*{margin:0;padding:0}
canvas{background:#d9d9d9;}
<canvas></canvas>

Animating lots of lines in WebGL

I'm trying to animate this scene at 1280x720, 60 fps, antialiased. There are ~500K triangles, two for each tree branch. I can't use gl.LINES because ANGLE.
To get a rough idea about the performance I can expect, I made this test with 1M of random triangles:
<!doctype html>
<head>
<title>Triangles</title>
<style>
html, body {
background: #000;
height: 100%;
margin: 0;
}
canvas {
width: 1280px;
height: 720px;
position: absolute;
margin: auto;
top: 0;
right: 0;
left: 0;
bottom: 0;
}
</style>
</head>
<body>
<script>
'use strict';
const triangleCount = 1e6;
const antialias = true;
const generateTriangles = (count, width, height) => {
const coords = new Float32Array(9 * count);
for (var i = 0; i < coords.length;) {
const x = Math.random() * 2 - 1;
const y = Math.random() * 2 - 1;
const z = Math.random() * 2 - 1;
const theta = Math.random() * Math.PI;
const ax = 10 * Math.cos(theta) / width;
const ay = 10 * Math.sin(theta) / height;
const bx = 10 * Math.cos(theta + 0.3) / width;
const by = 10 * Math.sin(theta + 0.3) / height;
coords[i++] = x + ax; coords[i++] = y + ay; coords[i++] = z;
coords[i++] = x + bx; coords[i++] = y + by; coords[i++] = z;
coords[i++] = x - ax; coords[i++] = y - ay; coords[i++] = z;
coords[i++] = x - ax; coords[i++] = y - ay; coords[i++] = z;
coords[i++] = x - bx; coords[i++] = y - by; coords[i++] = z;
coords[i++] = x + ax; coords[i++] = y + ay; coords[i++] = z;
}
return coords;
};
const vertexShaderSource = `
precision lowp float;
attribute vec3 aPosition;
uniform float uWobble;
void main() {
float p = 1.0 / (0.3 * aPosition.z - 1.4 + 0.1 * uWobble);
gl_Position = vec4(p * aPosition.x, p * aPosition.y, aPosition.z, 1.0);
}
`;
const fragmentShaderSource = `
precision lowp float;
void main() {
float z = gl_FragCoord.z;
gl_FragColor = vec4(1.2 * z, z * z, z, 1.0);
}
`;
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
const gl = canvas.getContext('webgl', {alpha: false, antialias});
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
const aVertexPosition = gl.getAttribLocation(program, 'aPosition');
gl.enableVertexAttribArray(aVertexPosition);
const uWobble = gl.getUniformLocation(program, 'uWobble');
gl.uniform1f(uWobble, 1);
const vertices = generateTriangles(triangleCount, canvas.width, canvas.height);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
const render = (timestamp) => {
requestAnimationFrame(render);
gl.uniform1f(uWobble, Math.sin(0.002 * timestamp));
gl.drawArrays(gl.TRIANGLES, 0, vertices.length / 3);
};
window.requestAnimationFrame(render);
</script>
</body>
On a Macbook with Iris 1536 MB I'm getting 12 frames per second. Are there any optimizations I'm missing, such as alternative ways to draw lines, faster antialiasing, some magic flags? Or is this basically the limit of what one can expect from an average GPU?

Set object drag limit in Fabric.js

i am new in fabric js want to set the drag limit
i have also try with https://github.com/kangax/fabric.js/wiki/Working-with-events
not able to get the solution.
please check the attached image, object can move anyware but it should be display in red area only.i want this. help me...thanks in advance !!
While Orangepill's answer is correct, it produces a "stuttering" when your object hits the object bounds. If you have a rectangular bounding box (and not a complex bounding object) an alternative is to allow the object to be dragged along the bounds and "slide" along the bounding box. You do this by capping the coordinates values and letting the other dimension move as usual. An example snippet would look like so:
var canvas = new fabric.Canvas("bounded");
var boundingBox = new fabric.Rect({
fill: "none",
width: 600,
height: 400,
hasBorders: false,
hasControls: false,
lockMovementX: true,
lockMovementY: true,
evented: false,
stroke: "red"
});
var movingBox = new fabric.Rect({
width: 100,
height: 100,
hasBorders: false,
hasControls: false
});
canvas.on("object:moving", function() {
var top = movingBox.top;
var bottom = top + movingBox.height;
var left = movingBox.left;
var right = left + movingBox.width;
var topBound = boundingBox.top;
var bottomBound = topBound + boundingBox.height;
var leftBound = boundingBox.left;
var rightBound = leftBound + boundingBox.width;
// capping logic here
movingBox.setLeft(Math.min(Math.max(left, leftBound), rightBound - movingBox.width));
movingBox.setTop(Math.min(Math.max(top, topBound), bottomBound - movingBox.height));
});
canvas.add(boundingBox);
canvas.add(movingBox);
See this example in JSFiddle here
Felix Fung's answer was a starting point, but there are many things to consider. Here is a version that accounts for some of them.
It handles the canvas having a viewport transform (ie, zoomed/panned) and objects that are center-origined instead of left/top-origined. It also constrains objects wider/taller than the viewport to the top/left instead of the bottom/right.
canvas.on("object:moving", function(e) {
var obj = e.target;
var canvas = obj.canvas;
var top = obj.top;
var left = obj.left;
var zoom = canvas.getZoom();
var pan_x = canvas.viewportTransform[4];
var pan_y = canvas.viewportTransform[5];
// width & height we are constraining to must be calculated by applying the inverse of the current viewportTransform
var c_width = canvas.width / zoom;
var c_height = canvas.height / zoom;
var w = obj.width * obj.scaleX
var left_adjust, right_adjust
if(obj.originX == "center") {
left_adjust = right_adjust = w / 2;
} else {
left_adjust = 0;
right_adjust = w;
}
var h = obj.height * obj.scaleY;
var top_adjust, bottom_adjust;
if(obj.originY == "center") {
top_adjust = bottom_adjust = h / 2;
} else {
top_adjust = 0;
bottom_adjust = h;
}
// if you need margins set them here
var top_margin = 0;
var bottom_margin = 0;
var left_margin = 0;
var right_margin = 0;
var top_bound = top_margin + top_adjust - pan_y;
var bottom_bound = c_height - bottom_adjust - bottom_margin - pan_y;
var left_bound = left_margin + left_adjust - pan_x;
var right_bound = c_width - right_adjust - right_margin - pan_x;
if( w > c_width ) {
obj.setLeft(left_bound);
} else {
obj.setLeft(Math.min(Math.max(left, left_bound), right_bound));
}
if( h > c_height ) {
obj.setTop(top_bound);
} else {
obj.setTop(Math.min(Math.max(top, top_bound), bottom_bound));
}
});
What had worked for me is to create an event listener for the object:moving event. When the move is happening you update the goodtop and goodleft variables and once you are out of bounds to reposition the object to the last good points.
var goodtop, goodleft, boundingObject;
canvas.on("object:moving", function(){
var obj = this.relatedTarget;
var bounds = boundingObject;
obj.setCoords();
if(!obj.isContainedWithinObject(bounds)){
obj.setTop(goodtop);
obj.setLeft(goodleft);
canvas.refresh();
} else {
goodtop = obj.top;
goodleft = obj.left;
}
});
I used Michael Johnston's snipped as a starting point to add bounding control for rotated elements. This snipped only covers the cases when either (obj.centerX && obj.centerY == "center") || (obj.centerX && obj.centerY != "center")
canvasRef.current.on("object:moving", function (e) {
var obj = e.target;
var canvas = obj.canvas;
var zoom = canvas.getZoom();
var pan_x = canvas.viewportTransform[4];
var pan_y = canvas.viewportTransform[5];
// get left, top, width, and height of object
var left = obj.left;
var top = obj.top;
var width = obj.width * obj.scaleX;
var height = obj.height * obj.scaleY;
// width & height we are constraining to must be calculated by applying the inverse of the current viewportTransform
var c_width = canvas.width / zoom;
var c_height = canvas.height / zoom;
// calculate values that define the origin of the object, when it is centered in the center or not
var left_adjust, right_adjust;
if (obj.originX == "center") {
left_adjust = right_adjust = width / 2;
} else {
left_adjust = 0;
right_adjust = width;
}
var top_adjust, bottom_adjust;
if (obj.originY == "center") {
top_adjust = bottom_adjust = height / 2;
} else {
top_adjust = 0;
bottom_adjust = height;
}
// support for rotated objects
if (obj.angle) {
var angle = obj.angle;
if (angle > 270) {
angle -= 270;
} else if (angle > 180) {
angle -= 180;
} else if (angle > 90) {
angle -= 90;
}
const radians = angle * (Math.PI / 180);
const w_opposite = width * Math.sin(radians);
const w_adjacent = width * Math.cos(radians);
const h_opposite = height * Math.sin(radians);
const h_adjacent = height * Math.cos(radians);
if (obj.originX != "center" && obj.originY != "center") {
if (obj.angle <= 90) {
left_adjust = h_opposite;
top_adjust = 0;
right_adjust = w_adjacent;
bottom_adjust = h_adjacent + w_opposite;
} else if (obj.angle > 90 && obj.angle <= 180) {
left_adjust = h_adjacent + w_opposite;
top_adjust = h_opposite;
right_adjust = 0;
bottom_adjust = w_adjacent;
} else if (obj.angle > 180 && obj.angle <= 270) {
left_adjust = w_adjacent;
top_adjust = w_opposite + h_adjacent;
right_adjust = h_opposite;
bottom_adjust = 0;
} else {
left_adjust = 0;
top_adjust = w_adjacent;
right_adjust = w_opposite + h_adjacent;
bottom_adjust = h_opposite;
}
}
if (obj.originX == "center" && obj.originY == "center") {
if (obj.angle <= 90 || (obj.angle > 180 && obj.angle <= 270)) {
left_adjust = (w_adjacent + h_opposite) / 2;
right_adjust = (w_adjacent + h_opposite) / 2;
top_adjust = (h_adjacent + w_opposite) / 2;
bottom_adjust = (h_adjacent + w_opposite) / 2;
} else {
left_adjust = (h_adjacent + w_opposite) / 2;
right_adjust = (h_adjacent + w_opposite) / 2;
top_adjust = (w_adjacent + h_opposite) / 2;
bottom_adjust = (w_adjacent + h_opposite) / 2;
}
}
}
// if you need margins set them here
var top_margin = 0;
var bottom_margin = 0;
var left_margin = 0;
var right_margin = 0;
var top_bound = top_margin + top_adjust - pan_y;
var bottom_bound = c_height - bottom_adjust - bottom_margin - pan_y;
var left_bound = left_margin + left_adjust - pan_x;
var right_bound = c_width - right_adjust - right_margin - pan_x;
if (width > c_width) {
obj.set("left", left_bound);
} else {
obj.set("left", Math.min(Math.max(left, left_bound), right_bound));
}
if (height > c_height) {
obj.set("top", top_bound);
} else {
obj.set("top", Math.min(Math.max(top, top_bound), bottom_bound));
}
});
SEE THE WORKING EXAMPLE
It's as simple as a water try this
just use this js
<script type="text/javascript">
//use global variable for canvas object
var canvas;
var ctx;
function onLoad() {
//get fabric canvas with id mycanvas
canvas = new fabric.Canvas('mycanvas');
canvas.on("mouse : down",function{
//get canvas 2d context
ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.rect(115,60,221,390);//specify bounded rectangle
ctx.closePath();
ctx.clip();
ctx.save();
});
//now restore the context on mouse up
canvas.on("mouse : up",function(){
ctx.restore();//restore the context
});
}
</script>
Hope this will help you.
Njoy coding :)
now u can have the same clipping region for every object supported by Fabric.js for transformation and moving.
try it :).

Flip shape with image attached using RaphaelJS

I managed to create a hexagon with Raphel, gave it a stroke and a fill image, then (with help from StackOverFlow) managed to create a flip animation on the hexagon.
But I am facing a problem. When the hexagon flips, the background image does not flip with it. How can I make the hexagon shape flip, but as it is flipping, show the background image flipping too.
Here is what I currently have:
function polygon(x, y, size, sides, rotate) {
var self = this;
self.centrePoint = [x,y];
self.size = size;
self.sides = sides;
self.rotated = rotate;
self.sizeMultiplier = 50;
self.points = [];
for (i = 0; i < sides; i++) {
self.points.push([(x + (self.size * self.sizeMultiplier) * (rotate ? Math.sin(2 * 3.14159265 * i / sides) : Math.cos(2 * 3.14159265 * i / sides))), (y + (self.size * self.sizeMultiplier) * (rotate ? Math.cos(2 * 3.14159265 * i / sides) : Math.sin(2 * 3.14159265 * i / sides)))]);
}
self.svgString = 'M' + self.points.join(' ') + ' L Z';
}
$(document).ready(function() {
var paper = Raphael(0, 0, 450, 450);
var path1 = new polygon(100, 100, 2, 6, false);
var hex1 = paper.path(path1.svgString);
hex1.node.id = "hex1";
hex1.attr("fill", "url('http://i49.tinypic.com/23ma7pt.jpg')");
hex1.attr("stroke", "#aceace");
/* flip animation */
hex1.click(function() {
hex1.animate({transform: "S0,1"},500,'easeIn', function()
{
hex1.attr("fill","url('http://i49.tinypic.com/23ma7pt.jpg')");
hex1.animate({transform: "S-1,1"},500,'easeOut');
});
});
});

Resources