var i = 0;
var x = 960;
var y = 540;
var interval = window.setInterval(render, 100);
function render()
{
gradient = cxt.createRadialGradient(x, y, i, x, y, i+10);
gradient.addColorStop(0, "rgba(0,0,0,0)");//white inside
gradient.addColorStop(.4, "rgba(255,255,255,1)");//white inside
gradient.addColorStop(.6, "rgba(255,255,255,1)");//white inside
gradient.addColorStop(1, "rgba(0,0,0,0)");//fade to transparent black outside;
cxt.fillStyle= "#000000";
cxt.fillRect(0,0,WIDTH,HEIGHT);
cxt.fillStyle = gradient;
cxt.fillRect(0,0,WIDTH,HEIGHT);
cxt.fill();
cxt.drawImage(logo,0,0);
i+=5;
}
I'm trying to animate a feathered loop expanding.
This code is pretty inefficient, because I'm using the constructor to change as single property each loop. How can I change a single property that is passed as a parameter to the constructor?
From the Canvas specs...
interface CanvasGradient {
// opaque object
void addColorStop(in float offset, in DOMString color);
};
...and earlier it says about fillStyle and strokeStyle...
On getting, if the value is a color,
then the serialization of the color
must be returned. Otherwise, if it is
not a color but a CanvasGradient or
CanvasPattern, then the respective
object must be returned. (Such objects
are opaque and therefore only useful
for assigning to other attributes or
for comparison to other gradients or
patterns.)
Lastly, introspecting a gradient just reveals the addColorStop function.
So I think the constructor is the only place those values can be set; but are you sure the constructor is really slowing it down? If your animation is slow maybe it's something else. Have you timed it?
Related
I am new to Javascript. I am trying to create a circle filled with white on one color stop and a random color for the second color stop to create a radial gradient effect. However, I am getting this error: Uncaught DOMException: Failed to execute 'addColorStop' on 'CanvasGradient': The value provided ('undefined') could not be parsed as a color.
I would appreciate any suggestions.
I tried to break it down into 2 steps.
The first step was to create a circle filled with two colors (white: inner, pink:outer) to create a radial gradient effect which I did.
https://jsfiddle.net/RE006/sv8guk62/103/
The second step: which is where I am coming across problems:
https://jsfiddle.net/RE006/sv8guk62/105/
My goal was to create a circle filled with two colors (white: inner, random color: outer) to create a radial gradient effect.
Error: Uncaught DOMException: Failed to execute 'addColorStop' on 'CanvasGradient': The value provided ('undefined') could not be parsed as a color.
HTML:
<canvas id="circle"></canvas>
Javscript:
document.addEventListener('DOMContentLoaded', function() {
var c = document.getElementById("circle");
var ctx = c.getContext("2d");
//Arc(x, y, width, height, startAngle, arcAngle)
fillArc(100, 70, 70, 0, 6.28);
function fillArc() {
ctx.beginPath();
ctx.arc.apply(ctx, arguments);
}
function newGradient() {
var randomColor1 = "#" + Math.floor(Math.random()*16777215).toString(16);
}
// Create gradient
// (x0,y0,r0 (starting circle),x1,y1, r1(ending circle))
var grd = ctx.createRadialGradient(100,60,5,100,60,50);
grd.addColorStop(0,"white");
grd.addColorStop(1,newGradient());
// Fill with gradient
ctx.fillStyle = grd;
ctx.fill();
});
In newGradient function you forgot to return the value
I wanna be able to move around each individual vertex of an obj model that i imported into p5..so i wanna be able to access the coordinates of each individual vertex. Is this possible to do??
I tried console logging the model and finding the coordinates there, but didnt find anything
The p5.Geometry object has a vertices property which is a list of p5.Vector objects. You can modify these vertices, however when you make changes you also need to update the gid property to invalidate cached buffers based on the original list. Also this only allows you to modify existing triangles, adding and removing triangles is more complicated and involves the vertexNormals and faces properties as well.
//draw a spinning teapot
let teapot;
let version = 0;
function preload() {
// Load model with normalize parameter set to true
teapot = loadModel('https://www.paulwheeler.us/files/teapot.obj', true);
}
function setup() {
createCanvas(400, 400, WEBGL);
describe('Vertically rotating 3-d teapot with red, green and blue gradient. Double click to mutate vertices.');
}
function doubleClicked() {
// randomly translate the vertices.
for (const v of teapot.vertices) {
v.x *= random(0.95, 1.05);
v.y *= random(0.95, 1.05);
v.z *= random(0.95, 1.05);
}
teapot.computeNormals();
// Update the geometry id so that the vertex buffer gets recreated
// Without this p5.js will reused the cached vertex buffer.
teapot.gid = `teapot-version-${version++}`;
}
function draw() {
background(200);
scale(1.6); // Scaled to make model fit into canvas
rotateX(frameCount * 0.01);
rotateY(frameCount * 0.01);
normalMaterial(); // For effect
model(teapot);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
So I've gotten pretty excited about the introduction of Canvas Paths as standard objects in contemporary browsers, and have been trying to see how much mileage I can get out of this new-ish feature. However, my understanding of how these objects interact with the isPointInPath() method (and possibly other path-based methods) is apparently somewhat flawed.
As demonstrated in the first two test functions below, I can get the drawn paths to be recognized by the isPointInPath() method. However, when I define the paths as an object, the method ceases to work (even though the path objects can be recognized for other purposes such as filling).
function startGame(){ //Initiating Environment Variables
gamemap = document.getElementById("GameMap")
ctx = gamemap.getContext("2d")
testCircleBounds()
testVarCircleBounds()
testObjCircleBounds()
testMultiObjCircleBounds()
}
function testCircleBounds() { //Minimalist Test of Path Methods
ctx.beginPath()
ctx.arc(250,250,25,0,2*Math.PI)
console.log(ctx.isPointInPath(250,250)) //point in path detected
ctx.closePath()
console.log(ctx.isPointInPath(250,250)) //point in path still detected
ctx.stroke()
ctx.fillStyle = "yellow"
ctx.fill() //fills great
}
function testVarCircleBounds() { //Test of Path Methods with Variables
x_cen = 250; y_cen = 250; rad = 15
ctx.beginPath()
ctx.arc(x_cen,y_cen,rad,0,2*Math.PI)
ctx.closePath()
console.log(ctx.isPointInPath(x_cen,y_cen)) //true yet again
ctx.stroke()
ctx.fillStyle = "orange"
ctx.fill() //also fills great
}
function testObjCircleBounds() { //Test of Path Methods with Single Stored Path Object
x_cen = 250; y_cen = 250; rad = 10
ctx.beginPath()
lonely_node = new Path2D()
lonely_node.arc(x_cen,y_cen,10,0,2*Math.PI)
ctx.closePath()
console.log(ctx.isPointInPath(x_cen,y_cen)) //point in path not found!
ctx.stroke(lonely_node)
ctx.fillStyle = "red"
ctx.fill(lonely_node) //but ctx.fill notices the path just fine
}
function testMultiObjCircleBounds(){ //Test of Paths Methods with Multi-Object Referencing
nodes = [] //initializes set of nodes as array
for (i=0; i<25; i++) { //generates 25 nodes
nodes[i] = new Path2D() //defines each node as Path object in the array
node = nodes[i]
//Places Nodes along the 'horizon' of the map
x_cen = 20*i + 10
y_cen = 100
ctx.beginPath(node) //"node" argument probably not helping?
node.arc(x_cen,y_cen,8,0,2*Math.PI)
console.log(ctx.isPointInPath(x_cen,y_cen)) //still returns false!
ctx.closePath(node)
ctx.stroke(node)
console.log(ctx.isPointInPath(x_cen,y_cen)) //arrgh!!
}
// Fill can also be selectively applied to referenced path objects
for (i=0; i<25; i=i+2) {
ctx.fill(nodes[i])
}
}
<!DOCTYPE html>
<html>
<head>
<title>Wrap Around Beta</title>
<script src="Circuity_PathObjectTest.js"></script>
</head>
<body onload='startGame()'>
<canvas id="GameMap" width="500" height="500" style="border:1px solid #000000"></canvas>
</body>
</html>
Is this fundamentally the wrong way to think about Path2D objects and to record 'hit' areas on a canvas? If so, is there another technique (saving the canvas context for each path drawn or something along that vein) that would produce the desired effect?
You must send a reference to the Path2D being tested into isPointInPath:
ctx.isPointInPath( lonely_node, x_cen, y_cen )
Canvas {
id: canvas
onPaint: {
if (personalInfo.count === 0) {
return
}
var ctx = canvas.getContext("2d");
ctx.globalCompositeOperation = "source-over";
var points = []
for (var i = 0; i < personalInfoModel.dataCount(); i++) {
var temp = personalInfoModel.get(i)
points.push({
date: temp.date,
heartRate: temp.heartRate,
temprature: temp.temprature,
pressure: temp.bloodPressure
}
)
}
drawAxis(ctx)
drawGridLineAndUnitNum(ctx, chart.activeChart, points, "x", 15);
}
}
I have two button. If button A is clicked, then set chart.activeChart to 7 and call cavas.requestPaint() on A::onClicked, on cavas.drawGridLineAndUnitNum draw seven vertical line. If button B is clicked besides set chart.activeChart to 30, all same to A::onClicked. I hope that when A is clicked, canvas wipe the drawn line which product by B is clicked and vice versa. But in fact, it always reserve the line draw by last time.
A Context2D, associated to a specific Canvas, provides two useful functions:
fillRect
clearRect
In most cases, it could be possible to "clear" a Canvassimply by filling it with the background color, i.e. by using fillRect. That's the approach of the StocQt example, which has a white background.
However, if the background is transparent, filling it does not remove other strokes and thus does not make much sense. In this case, the only possible way to clear the Canvas is by removing all the strokes, i.e. by using clearRect.
I use a transparent background and thus clearRect is the way to go for me.
Given: an html canvas context:
Example code: http://jsfiddle.net/m1erickson/wJ67M/
This code creates a CanvasGradient object in the gradient variable.
var gradient=context.createLinearGradient(0,0,300,150);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'white');
This code creates a CanvasGradient object in the gradient variable.
var gradient=context.createRadialGradient(150,150,30, 150,150,100);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'white');
This code creates a CanvasPattern object in the pattern variable.
var pattern=context.createPattern(myImage,'repeat');
Is there a programatic way of extracting the properties from these objects after they are created (not by referring back to the JS code that created them)?
Wanted:
The linear gradients line segment (0,0,300,150) and its colorstops (0,red,1,white).
The radial gradients circles (150,150,30, 150,150,100) and its colorstops (0,red,1,white).
The patterns image and repeat properties.
Thanks for any thoughts!
The canvas specs does not grant access to the inner gradient or pattern properties, just like, as you also know, one cannot get access to the transform matrix.
So the only solution is to inject CanvasRenderingContext2D, CanvasGradient and CanvasPattern prototypes to store, in the created objects, the values used to create them.
So for the Canvas, you can write something like :
// save native linear gradient function
var nativeCreateLinearGradient = CanvasRenderingContext2D.prototype.createLinearGradient ;
// redefine createLinearGradient with a function that stores creation data
// new properties : gradientType, x0, y0, x1, y1, colorStops
CanvasRenderingContext2D.prototype.createLinearGradient = function(x0, y0, x1, y1) {
// actually create the gradient
var newGradient = nativeCreateLinearGradient.apply(this,arguments);
// store creation data
newGradient.gradientType = 0 ; // 0 for linear, 1 for radial
newGradient.x0 = x0; newGradient.y0 = y0;
newGradient.x1 = x1; newGradient.y1 = y1;
newGradient.colorStops = [];
return newGradient;
};
And for the Gradient :
var dummyContext = document.createElement('canvas').getContext('2d');
var nativeAddColorStop = CanvasGradient.prototype.addColorStop;
CanvasGradient.prototype.addColorStop = function (offset, color) {
// evaluate offset (to avoid reference issues)
offset = +offset;
// evaluate color (to avoid reference issues)
dummyContext.fillStyle = color;
color = dummyContext.fillStyle ;
// store color stop
this.colorStops.push([offset, color]);
// build the real gradient
nativeAddColorStop.call(this, offset, color);
return this;
};
You can do this in a very similar way for the radial gradient, and for the pattern you might want to copy the image, which type is CanvasImageSource )