I have a two radial gradient in canvas HTML5 and when I combine the two shapes, their colors combine, however, I want the colors to be separate, like in the following example:
http://jsfiddle.net/Kk2tY/
I need as a minimum the transparency of color similar to this :
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var rgba = new Array();
rgba[0] = new Array();
rgba[0][0] = [0, 255, 163, 0, 0.09];
rgba[0][1] = [0.3, 255, 168, 0, 0.07];
rgba[0][2] = [0.6, 255, 156, 0, 0.04];
rgba[0][3] = [1, 255, 169, 0, 0];
var x = 100,
y = 75,
// Radii of the white glow.
innerRadius = 5,
outerRadius = 70,
// Radius of the entire circle.
radius = 60;
ctx.beginPath();
var gradient = ctx.createRadialGradient(x, y, innerRadius, x, y, outerRadius);
gradient.addColorStop(0, "rgba(255,163,0,0.35)");
gradient.addColorStop(0.3, "rgba(255,168,0,0.24)");
gradient.addColorStop(0.6, "rgba(255,156,0,0.17)");
gradient.addColorStop(1, "rgba(255,169,0,0)");
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.fillStyle = gradient;
ctx.fill();
ctx.beginPath();
var gradient = ctx.createRadialGradient(x + 45, y, innerRadius, x + 45, y, outerRadius);
gradient.addColorStop(0, "rgba(255,163,0,0.35)");
gradient.addColorStop(0.3, "rgba(255,168,0,0.24)");
gradient.addColorStop(0.6, "rgba(255,156,0,0.17)");
gradient.addColorStop(1, "rgba(255,169,0,0)");
ctx.arc(x + 45, y, radius, 0, 2 * Math.PI);
ctx.fillStyle = gradient;
ctx.fill();
http://jsfiddle.net/3Atnt/
I have also tried with context.globalAlpha but the result was the same.
When you use transparency, then all the different colors of a pixel will be added to get the final result.
If that's not the desired result, you're on your own.
What you need to do is draw a diagram (on paper) where you select various points and determine manually which color they should have.
Then look at all the points and try to come up with an algorithm that produces those results.
Finally, you need to convert that algorithm into JavaScript probably using a "pixel-by-pixel" rendering approach.
Another approach is to render the two shapes into two images and then use image processing to combine them according to certain rules. Google for html5 canvas image processing.
Related
This question already has an answer here:
HTML Canvas gradient only show one color
(1 answer)
Closed 3 months ago.
I want to scale the gradient without changing its container.
For example, apply scale or rotate on the created gradient object.
const ctx = document.body.appendChild(document.createElement('canvas')).getContext('2d')
gradient = ctx.createRadialGradient(75, 75, 10, 75, 75, 50)
gradient.addColorStop(0, 'red')
gradient.addColorStop(1, 'green')
ctx.fillStyle = gradient
//how change scaleX or scaleY or rotation from gradient object?!
//draw main container
ctx.fillRect(0, 0, 150, 150)//don't change scale or size
I don't know how you would go about rotating a radial gradient, but scaling it is pretty simple. Just change r0 to change the radius of the pure red circle, and change r1 to change where pure green starts. In between these two values the colors are interpolated, which makes the gradient. Hope this helps.
const canvas = document.body.appendChild(document.createElement('canvas'));
const ctx = canvas.getContext("2d");
var r0 = 10;
var r1 = 50;
var gradient = ctx.createRadialGradient(75, 75, r0, 75, 75, r1);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'green');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 150, 150);
I would like to create a circle with a special shadow effect.
Like this one:
.
It should look like a cone in wood or metal.
I tried to do something with the radial gradiant in canvas but i can't creat that special Effekt.
I don´t know how to create the this shadow effect.
Can somebody give me a tip or help me?
This is what I've tried: JSFiddle
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 100,
y = 75,
innerRadius = 1,
outerRadius = 70,
radius = 60;
ctx.arc(x, y, radius, 0, 2 * Math.PI);
var gradient = ctx.createRadialGradient(x, y, innerRadius, x, y, outerRadius);
gradient.addColorStop(0, '#FF9900');
gradient.addColorStop(1, '#FFFFFF');
ctx.fillStyle = gradient;
ctx.fill();
Greetings from Germany
Matzuman
There is unfortunately no gradient type in canvas which allow you to specify a radiant gradient. You have to provide a mechanism to do so manually.
You could use a shadow approach drawing the object off-screen while offsetting the shadow so it overlapped the cone base. One for light and one for dark side.
You can achieve a better effect though by drawing a "light/dark stripe" rotated around the center at varying opacity levels depending on angle.
Example "rendering" the cone
This example allow you to adjust parameters like how visible the reflections should be, what colors, size of cone etc. Experiment with the values to find what you're after.
To offset the "light source", just rotate one time initially with the angle you need before rendering the overlapping stripes.
var ctx = document.querySelector("canvas").getContext("2d"),
cx = 75, cy = 75, radius = 70, // for arc/cone
maxOpacity = 1, // max opacity (will accumulate)
angleStep = 0.01, // "resolution"
angle = 0, t; // current angle and opacity
// draw base of cone
ctx.fillStyle = "rgb(139, 108, 33)";
ctx.arc(cx, cy, radius, 0, 2*Math.PI);
ctx.fill();
// now rotate around center drawing a white stripe at varying opacity
// depending on angle
ctx.fillStyle = "rgb(181, 159, 109)";
ctx.translate(cx, cy); // pivot for rotation = center of cone
// half of the cone is done with white overlay
for(angle = 0; angle < Math.PI; angle += angleStep) {
// calculate t [0,1] based on angle. Multiply with max opacity
t = (angle < Math.PI * 0.5 ? angle : Math.PI - angle) / Math.PI * maxOpacity;
ctx.rotate(angleStep); // increase angle by step
ctx.globalAlpha = t; // set opacity to t
drawStripe(); // draw a small segment / "stripe"
}
// the other half of the cone is done with dark overlay
ctx.fillStyle = "rgb(95, 54, 5)";
for(angle = 0; angle < Math.PI; angle += angleStep) {
t = (angle < Math.PI * 0.5 ? angle : Math.PI - angle) / Math.PI * maxOpacity;;
ctx.rotate(angleStep);
ctx.globalAlpha = t;
drawStripe();
}
function drawStripe() {
ctx.beginPath();
ctx.lineTo(0, 0);
ctx.arc(0, 0, radius, 0, angleStep*5);
ctx.fill();
}
// top off by drawing a smaller circle on top
ctx.setTransform(1,0,0,1,0,0); // reset transforms
ctx.globalAlpha = 1; // reset alpha
ctx.fillStyle = "rgb(130, 97, 32)"; // draw in a topping
ctx.beginPath();
ctx.arc(cx, cy, radius * 0.25, 0, 2*Math.PI);
ctx.fill();
<canvas></canvas>
Example approximating a cone shape with shadows
var ctx = document.querySelector("canvas").getContext("2d"),
cx = 75, cy = 75, radius = 70, offset = radius * 2;
// draw base of cone
ctx.fillStyle = "rgb(139, 108, 33)";
ctx.arc(cx, cy, radius, 0, 2*Math.PI);
ctx.fill();
// offset next shape, couter-offset its shadow
ctx.translate(cx, offset*2); // make sure shape is drawn outside
ctx.scale(0.75, 1); // make shadow more narrow
ctx.globalCompositeOperation = "source-atop"; // comp. on top of existing pixels
ctx.shadowOffsetY = -offset * 1.1; // counter-offset shadow
ctx.shadowBlur = 25; // some blur
ctx.shadowColor = "rgba(181, 159, 109, 1)"; // highlight color
ctx.beginPath(); // draw new shape
ctx.arc(0, 0, radius * 0.6, 0, 2*Math.PI); // reduce radius ~50%
ctx.fill();
ctx.shadowOffsetY = -offset * 1.8; // counter-offset shadow
ctx.shadowColor = "rgba(95, 54, 5, 0.7)"; // shadow
ctx.beginPath(); // draw new shape
ctx.arc(0, 0, radius * 0.6, 0, 2*Math.PI); // reduce radius ~50%
ctx.fill();
// top off by drawing a smaller circle on top
ctx.setTransform(1,0,0,1,0,0); // reset transforms
ctx.globalCompositeOperation = "source-over"; // reset comp. mode
ctx.fillStyle = "rgb(130, 97, 32)"; // draw in a topping
ctx.beginPath();
ctx.arc(cx, cy, radius * 0.25, 0, 2*Math.PI);
ctx.fill();
<canvas></canvas>
Try using
ctx.shadowBlur = 40;
ctx.shadowColor = "#FF9900";
before drawing the circle.
shadowBlur sets the size of the shadow, you can set it to 0 if you want to disable it.
shadowColor is pretty self explanatory.
I'm trying to learn how to draw/fill different shapes by using canvas and JavaScript, but my shapes doesn't get filled in the way I want them to, at all. The body of my HTML-document is this simple line:
<canvas id="canvas1" width="500" height="500"></canvas>
And my JavaScript-file looks like this:
function draw() {
var canvas1 = document.getElementById('canvas1');
if(canvas1.getContext) {
var ctx = canvas1.getContext('2d');
var gradient = ctx.createLinearGradient(0, 0, 50, 0);
gradient.addColorStop(0, "blue");
gradient.addColorStop(1, "white");
ctx.beginPath();
ctx.moveTo(25,25);
ctx.lineTo(100, 25);
ctx.stroke();
ctx.moveTo(25, 50);
ctx.bezierCurveTo(25, 50, 50, 80, 75, 60)
ctx.fillStyle = "black";
ctx.fill();
ctx.beginPath();
ctx.moveTo(75, 100);
ctx.arc(50, 100, 25, 0, Math.PI*2, true);
ctx.fillStyle = "black";
ctx.fill();
ctx.beginPath();
ctx.fillStyle = gradient;
ctx.arc(75, 150, 25, 0, Math.PI*2, true);
ctx.fill();
}
}
But this is the result:
And I don't get it. I've tried filling my second circle with every other color, and that works just fine. Also if I remove the last "ctx.beginPath();" my first circle gets painted in gradient. But I can't get the same bug to work on my second circle by changing the position of the code or something. And every guide I've found tells me that this should work, as far as I understand it.
Gradients are defined with an absolute position so if you draw your circle outside the area defined by the gradient it will appear transparent instead of filled.
There is no need to close the path as the fill() method will close it implicit for you, but just make sure the coordinates in the gradient covers the area you want to fill.
Instead of calculating for each time you need to fill an arc you could create a generic wrapper function which takes a position and colors to fill (adjust as needed):
A demo here
/**
* Fills a circle with a two-color gradient.
* #param {Number} cx - center X
* #param {Number} cy - center Y
* #param {Number} radius - radius
* #param {String} col1 - start color as CSS color string
* #param {String} col2 - end color as CSS color string
* #param {Boolean} [horiz=false] - Set true for horizontal gradient
*/
function fillCircle(cx, cy, radius, col1, col2, horiz) {
var x = cx - radius,
y = cy - radius,
d = radius * 2,
gradient;
if (horiz) {
gradient = ctx.createLinearGradient(x, 0, x+d, d);
}
else {
gradient = ctx.createLinearGradient(0, y, 0, y+d);
}
gradient.addColorStop(0, col1);
gradient.addColorStop(1, col2);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, 2*Math.PI);
ctx.fill();
}
Then just use it this way:
fillCircle(200, 200, 70, 'yellow', 'red');
The last flag is optional here and makes a horizontal gradient if set to true.
Use ctx.closePath(); After each separate shape/line you want is done.
ctx.beginPath();
ctx.moveTo(25, 50);
ctx.bezierCurveTo(25, 50, 50, 80, 75, 60)
ctx.strokeStyle = "black";
ctx.stroke();
ctx.closePath();
The gradient needs to be set with the coordinates matching where your shape is on the canvas.
You have the gradient starting at 0,0,
var gradient = ctx.createLinearGradient(0, 0, 50, 0);
But your circle is locates at 25,50. Make your gradient coordinates the same as you circle coordinates.
http://jsfiddle.net/bC75t/1/
I'm working with some canvas and processing.js but i cant figure out how to fill an arc/ellipse etc with an image.
Using JavaScript usually i do something like this:
ctx.save();
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2, true);
ctx.closePath();
ctx.clip();
ctx.drawImage(thumbImg, 0, 0, 400, 400);
ctx.beginPath();
ctx.arc(x, y, size, Math.PI * 2, true);
ctx.clip();
ctx.closePath();
ctx.restore();
and the game is done, but how can i do it with processing.js?
I've tried those options but seems that I'm doing something wrong:
b = loadImage("nicola.png");
fill(b)
background(b);
ellipse(x, y, size, size);
any idea?
I believe that what you are trying to get at is called image masking
an example of masking
Description:
Masks part of an image from displaying by loading another image and using it as an alpha channel. This mask image should only contain grayscale data, but only the blue color channel is used. The mask image needs to be the same size as the image to which it is applied.
In addition to using a mask image, an integer array containing the alpha channel data can be specified directly. This method is useful for creating dynamically generated alpha masks. This array must be of the same length as the target image's pixels array and should contain only grayscale data of values between 0-255.
Example:
var g2;
var setup = function(){
createCanvas(200,200);
background(0, 0, 0, 0);
smooth();
fill(255, 255, 255);
ellipse(100, 100, 200, 200);
var g1 = get(0, 0, 200, 200);
background(0, 0, 0, 0);
noStroke();
for(let i = 0; i < 360; i++){
fill(sin(radians(i))*255, i, 200);
rect(0, i, 200, 1);
}
g2 = get(0, 0, 200, 200);
g2.mask(g1);
}
var draw = function(){
background(255, 255, 255);
image(g2, 0, 0);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>
an image of what the above code returns:
You can either use img.mask(maskImg) to apply an (pixel based) alpha mask or use img.blend(…) as described here for example.
A semicolon ';' is missing after fill(b)
So it should be fill(b);
I hope this solves your problem.
I create a simple circle with the arc function:
/* ctx is the 2d context */
ctx.beginPath();
var gradient = ctx.createLinearGradient(0, 0, this.radius, this.radius);
gradient.addColorStop(0, '#FF0000');
gradient.addColorStop(1, '#FFFFFF');
ctx.lineWidth = 10;
ctx.arc(this.radius, this.radius, this.radius, 0, 2*Math.PI, true);
ctx.strokeStyle = gradient;
ctx.stroke();
So I want to rotate the gradient, is that possible?
I've tried with ctx.rotate(x) but that rotates the whole circle!
Yes. Your gradient is going from x1,y1 to x2,y2, which are the four last arguments of createLinearGradient
For example, to reverse your gradient do this:
var gradient = ctx.createLinearGradient(this.radius, this.radius, 0, 0);
Or change it up however you please:
var gradient = ctx.createLinearGradient(this.radius, 0, 0, 0);
And so on.