Related
I am drawing five horizontal lines to an HMTL 5 2D canvas:
var canvas_ctx = my_canvas.getContext("2d");
canvas_ctx.lineWidth = 0.5;
canvas_ctx.strokeStyle = "black";
{
let line_x = 0;
let line_length = canvas_ctx.width;
let offset = 5;
let numLines = 5;
let numYincrement = 10;
for (let i=0;i<numLines * numYincrement;i+=numYincrement) {
//canvas_ctx.beginPath();
canvas_ctx.moveTo(line_x,i + offset);
canvas_ctx.lineTo(line_length,i + offset);
canvas_ctx.stroke();
//canvas_ctx.closePath();
}
}
This should, ideally result in 5 black lines. Instead, the color of the lines seems to fade with each new line (as if it's a gradient!), so that line 5 is gray. If I uncomment canvas_ctx.beginPath(); and canvas_ctx.closePath();, all lines become gray. Why is this happening??
Strokes do overlap from both sides of the coordinates.
var ctx = c.getContext('2d');
ctx.strokeStyle="red";
// draw big
ctx.scale(30, 30);
ctx.beginPath();
ctx.moveTo(5, 0);
ctx.lineTo(5, 10);
ctx.stroke();
drawPixelGrid();
function drawPixelGrid() {
// simply renders where the pixel bounds are
ctx.beginPath();
// remove the zoom
ctx.setTransform(1,0,0,1,0,0);
ctx.strokeStyle = 'gray';
ctx.lineWidth = 2; // avoid the problem we are demonstrating by using a perfect lineWidth ;-)
for(let y=0; y<=300; y+=30) {
ctx.moveTo(0, y);
ctx.lineTo(300, y);
for(let x=0; x<=300; x+=30) {
ctx.moveTo(x, 0);
ctx.lineTo(x, 300);
}
}
ctx.stroke();
}
<canvas id="c" height=300></canvas>
But obviously, a pixel can't be set to two colors at the same time. So browsers apply antialiasing, which will fade your pixel color to an other color, being the result of mixing the background and the foreground color.
So for a black stroke over a white or transparent background, this leads to actual gray pixels being rendered. Here I'll keep using red as an example:
var ctx = c.getContext('2d');
ctx.strokeStyle="red";
// first draw as on a 10*10 canvas
ctx.beginPath();
ctx.moveTo(5, 0);
ctx.lineTo(5, 10);
ctx.stroke();
// zoom it
ctx.imageSmoothingEnabled = 0;
ctx.globalCompositeOperation = 'copy';
ctx.drawImage(c, 0,0,9000,9000);
drawPixelGrid();
// this is not red...
function drawPixelGrid() {
ctx.globalCompositeOperation = 'source-over';
ctx.beginPath();
ctx.setTransform(1,0,0,1,0,0);
ctx.strokeStyle = 'gray';
ctx.lineWidth = 2;
for(let y=0; y<=300; y+=30) {
ctx.moveTo(0, y);
ctx.lineTo(300, y);
for(let x=0; x<=300; x+=30) {
ctx.moveTo(x, 0);
ctx.lineTo(x, 300);
}
}
ctx.stroke();
}
<canvas id="c" height=300></canvas>
One way to avoid it is generally to apply an offset on your coordinates so that the line extends correctly on pixels boundaries. E.g for a 1px lineWidth, you would apply a 0.5 offset:
var ctx = c.getContext('2d');
ctx.strokeStyle="red";
// first draw as on a 10*10 canvas
ctx.beginPath();
ctx.moveTo(5.5, 0); // offset +0.5px
ctx.lineTo(5.5, 10);
ctx.stroke();
// zoom it
ctx.imageSmoothingEnabled = 0;
ctx.globalCompositeOperation = 'copy';
ctx.drawImage(c, 0,0,9000,9000);
drawPixelGrid();
// now we've got a real red
function drawPixelGrid() {
ctx.globalCompositeOperation = 'source-over';
ctx.beginPath();
ctx.setTransform(1,0,0,1,0,0);
ctx.strokeStyle = 'gray';
ctx.lineWidth = 2;
for(let y=0; y<=300; y+=30) {
ctx.moveTo(0, y);
ctx.lineTo(300, y);
for(let x=0; x<=300; x+=30) {
ctx.moveTo(x, 0);
ctx.lineTo(x, 300);
}
}
ctx.stroke();
}
<canvas id="c" height=300></canvas>
But in your case, you are drawing at 0.5px lineWidth, so no offset will be able to get rid of this antialiasing.
So if you want perfect color, choose a correct lineWidth.
I want to change a line width but why all lines width in canvas is also changed?
Bellow is my code snippet
let c_canvas = document.getElementById("c");
let context = c_canvas.getContext("2d");
let gradientFill = context.createLinearGradient(400, 0, 95, 305);
gradientFill.addColorStop(0, "rgba(195, 42, 28, 1.000)");
gradientFill.addColorStop(0.6, "rgba(252, 239, 55, 1.000)");
gradientFill.addColorStop(1, "rgba(12, 151, 206, 1.000)");
context.fillStyle = gradientFill;
context.fillRect(0, 0, 500, 500);
context.beginPath();
for (let x = 0.5; x <= 501; x += 100) {
context.moveTo(x, 0);
context.lineTo(x, 500);
}
for (let y = 0.5; y <= 501; y += 100) {
context.moveTo(0, y);
context.lineTo(500, y);
}
context.lineWidth = 1;
context.stroke(); // Draw it
let frectx = 100;
let frecty = 450;
let lrectx = 250;
let lrecty = 340;
let radius = 15; // for example
let font = "bold " + radius + "px serif";
let text = "1";
let rand =[];
for(let i=0; i<5; i++)
{
rand[i] = Math.floor((Math.random() * 6) + 1);
}
rand.forEach(function(entry,i) {
text = i+1;
frectx = entry*70;
frecty = Math.floor((Math.random() * 9) + 1)*50;
context.moveTo(frectx, frecty);
context.lineTo(lrectx, lrecty);
context.lineWidth = 8;
context.strokeStyle = "#ddd";
context.stroke();
context.fillStyle = "white";
context.beginPath();
context.arc(frectx, frecty, 10, 0, 2 * Math.PI);
context.stroke();
context.closePath();
context.fill();
context.fillStyle = "black"; // font color to write the text with
context.font = font;
context.textBaseline = "top";
context.fillText(text, frectx - radius / 4, frecty - radius / 2);
context.fillStyle = "white";
context.beginPath();
context.arc(lrectx, lrecty, 10, 0, 2 * Math.PI);
context.stroke();
context.closePath();
context.fill();
context.fillStyle = "black"; // font color to write the text with
context.font = font;
context.textBaseline = "top";
context.fillText(text, lrectx - radius / 4, lrecty - radius / 2);
})
<canvas id="c" width="501px" height="501px"></canvas>
or you can see in jsfiddle:
https://jsfiddle.net/dyaskur/t4fgLs73/
How to only change width in that lines inside the box?
My second question is how to make my line and circle transform to glow/change color when i hover on it?
A context.beginPath() is missing between
context.lineWidth = 1;
context.stroke(); // Draw it
and
context.lineTo(lrectx, lrecty);
context.lineWidth = 8;
context.strokeStyle = "#ddd";
context.stroke();
Without the beginPath call you are simply re-stroking all or the paths and subpaths already defined with the new stroke style and width.
The general answer to the second part of your question is that you don't get to do this.
Painting a canvas is equivalent to drawing an image. You can work out where the mouse is over the image, but you would then need to work out (in your program) if the mouse is over some pixels you want to change and redraw the canvas if it is.
If you want to use CSS :hover pseudo classes to change presentation you will need to construct the source code of an SVG element for the graphic, create the element from the source code generated, and supply appropriate CSS for the SVG element's child nodes that will be affected by mouse position.
What I want is for there to be a button, and the background of the button to be a canvas. This is my button code:
//Lets create a simple particle system in HTML5 canvas and JS
//Initializing the canvas
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//Canvas dimensions
var W = 500; var H = 500;
//Lets create an array of particles
var particles = [];
for(var i = 0; i < 50; i++)
{
//This will add 50 particles to the array with random positions
particles.push(new create_particle());
}
//Lets create a function which will help us to create multiple particles
function create_particle()
{
//Random position on the canvas
this.x = Math.random()*W;
this.y = Math.random()*H;
//Lets add random velocity to each particle
this.vx = Math.random()*20-10;
this.vy = Math.random()*20-10;
//Random colors
var r = Math.random()*255>>0;
var g = Math.random()*255>>0;
var b = Math.random()*255>>0;
this.color = "rgba("+r+", "+g+", "+b+", 0.5)";
//Random size
this.radius = Math.random()*20+20;
}
var x = 100; var y = 100;
//Lets animate the particle
function draw()
{
//Moving this BG paint code insde draw() will help remove the trail
//of the particle
//Lets paint the canvas black
//But the BG paint shouldn't blend with the previous frame
ctx.globalCompositeOperation = "source-over";
//Lets reduce the opacity of the BG paint to give the final touch
ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
ctx.fillRect(0, 0, W, H);
//Lets blend the particle with the BG
ctx.globalCompositeOperation = "lighter";
//Lets draw particles from the array now
for(var t = 0; t < particles.length; t++)
{
var p = particles[t];
ctx.beginPath();
//Time for some colors
var gradient = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.radius);
gradient.addColorStop(0, "white");
gradient.addColorStop(0.4, "white");
gradient.addColorStop(0.4, p.color);
gradient.addColorStop(1, "black");
ctx.fillStyle = gradient;
ctx.arc(p.x, p.y, p.radius, Math.PI*2, false);
ctx.fill();
//Lets use the velocity now
p.x += p.vx;
p.y += p.vy;
//To prevent the balls from moving out of the canvas
if(p.x < -50) p.x = W+50;
if(p.y < -50) p.y = H+50;
if(p.x > W+50) p.x = -50;
if(p.y > H+50) p.y = -50;
}
}
setInterval(draw, 33);
//I hope that you enjoyed the tutorial :)
<button align=center>
<canvas id="canvas"></canvas>
<span id="submit">Submit</span>
</button>
For some reason, the button is huge, and I don't know why, but also, I want my text to be on top of the canvas. How can I do that?
You need to specify the size of the canvas. You can do so by setting the width and height of the canvas to a fixed value via attributes i.e <canvas width="50" height="50"></canvas>. The drawing is bound by the width and height variables which you may like to alter as well. As for the text, it needs to be positioned on top of the canvas using absolute positioning. Alternatively, you could draw text on the canvas directly. Note that you can use a canvas without the button and then register a click event handler on the canvas to simulate a button instead.
https://jsfiddle.net/684vtxm1/
I have small question about my concept in photoshop:
I need to create html5/css/js adwords banner, where they will levitate background images (red pentagons) but also at the forefront of the entire banner, because in addition to those levitating pictures there will be other objects (texts, buttons).
It is possible to achive this, and also it is possible change path of moving these red pentagons base base on cursor position if user will move his cursor on over that banner?
Edit:
I found this project: https://codepen.io/VIRU/pen/FAdkl
Can be edited for flying pictures?
window.onload = function() {
//Create canvas and initialize it's context
var canvas = document.getElementById("flying-bubbles");
var ctx = canvas.getContext("2d");
//Set the dimensions of canvas equal to the window's dimensions
var W = window.innerWidth, H = window.innerHeight;
canvas.width = W;
canvas.height = H;
//Create an array of circles
var circles = [];
for(var i = 0; i < 20; i++ ){
circles.push(new create_circle());
}
//Function to create circles with different positions and velocities
function create_circle() {
//Random Position
this.x = Math.random()*W;
this.y = Math.random()*H;
//Random Velocities
this.vx = 0.1+Math.random()*1;
this.vy = -this.vx;
//Random Radius
this.r = 10 + Math.random()*50;
}
//Function to draw the background
function draw() {
//Create the gradient
var grad = ctx.createLinearGradient(0, 0, W, H);
grad.addColorStop(0, 'rgb(19, 105, 168)');
grad.addColorStop(1, 'rgb(0, 0, 0)');
//Fill the canvas with the gradient
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = grad;
ctx.fillRect(0,0,W,H);
//Fill the canvas with the circles
for(var j = 0; j < circles.length; j++) {
var c = circles[j];
//Draw the circle and it with the blur grad
ctx.beginPath();
ctx.globalCompositeOperation = "lighter";
ctx.fillStyle = grad;
ctx.arc(c.x, c.y, c.r, Math.PI*2, false);
ctx.fill();
//Lets use the velocity now
c.x += c.vx;
c.y += c.vy;
//To prevent the circles from moving out of the canvas
if(c.x < -50) c.x = W+50;
if(c.y < -50) c.y = H+50;
if(c.x > W+50) c.x = -50;
if(c.y > H+50) c.y = -50;
}
}
setInterval(draw, 25);
}
http://codepen.io/zimon/pen/KNmKpN
Try to change circles drawing to the image.
var img = new Image();
img.src= 'http://www.media3.net/images/redsquare.png';
ctx.drawImage(img,c.x,c.y);
I'm currently using HTML5's canvas to render a number of strings using the fillText method. This works fine, but I'd also like to give each string a 1px black outer stroke. Unfortunately the strokeText function seems to apply an inner stroke. To counter this, I've written a drawStrokedText function that achieves the effect I'm after. Unfortunately it's horrible slow (for obvious reasons).
Is there a fast, cross-browser way of achieving a 1px outer stroke using native canvas functionality?
drawStrokedText = function(context, text, x, y)
{
context.fillStyle = "rgb(0,0,0)";
context.fillText(text, x-1, y-1);
context.fillText(text, x+1, y-1);
context.fillText(text, x-1, y);
context.fillText(text, x+1, y);
context.fillText(text, x-1, y+1);
context.fillText(text, x+1, y+1);
context.fillStyle = "rgb(255,255,255)";
context.fillText(text, x, y);
};
Here's an example of the effect at work:
What's wrong with stroke? Since half the stroke will be outside of the shape, you can always draw the stroke first with a line width of double what you want. So if you wanted a 4px outer stroke you could do:
function drawStroked(text, x, y) {
ctx.font = '80px Sans-serif';
ctx.strokeStyle = 'black';
ctx.lineWidth = 8;
ctx.strokeText(text, x, y);
ctx.fillStyle = 'white';
ctx.fillText(text, x, y);
}
drawStroked("37°", 50, 150);
Which makes:
live fiddle here: http://jsfiddle.net/vNWn6/
IF that happens to not look as accurate at smaller text rendering scales, you can always draw it large but scale it down (in the above case you'd do ctx.scale(0.25, 0.25))
Simon's answer is a good solution, yet it may have mitering glitches in some cases, especially with capital 'M', 'V', & 'W':
drawStroked("MVW", 50, 150);
http://jsfiddle.net/hwG42/1/
In this case, it's best to utilize:
ctx.miterLimit=2;
http://jsfiddle.net/hwG42/3/
Best of luck!
The above answers are great, using some of these solutions* and some of my own ideas, I made a quick reference and some creative alternatives in the below fiddle.
*All credits given where due in the fiddle code
drawStrokedText ( text, x, y );
drawShadowedText ( text, x, y, shadowBlur);
drawGlowingText ( text, x, y, glowColorHex, glowDistance);
drawBlurredText ( text, x, y, blurAmount);
drawReflectedText ( text, x, y, reflectionScale, reflectionOpacity);
https://jsfiddle.net/vtmnyea8/
// Author: Aaron Edmistone
// Text effects using HTML5 Canvas with 2D Context.
// https://stackoverflow.com/questions/7814398/a-glow-effect-on-html5-canvas
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
// prepare the presentation of the canvas
ctx.fillStyle = 'black';
ctx.fillRect(0,0,250,450);
ctx.fillStyle = 'gray';
ctx.fillRect(250,0,250,450);
ctx.fillStyle = 'white';
ctx.fillRect(500,0,250,450);
ctx.fillStyle = '#0066CC';
ctx.fillRect(750,0,250,450);
// prepare the font and fill
ctx.font = "80px Sans-serif";
ctx.fillStyle = "white";
function drawStrokedText(text, x, y)
{
// using the solutions from #Simon Sarris and #Jackalope from
// https://stackoverflow.com/questions/7814398/a-glow-effect-on-html5-canvas
ctx.save();
ctx.strokeStyle = 'black';
ctx.lineWidth = 8;
ctx.lineJoin="round";
ctx.miterLimit=2;
ctx.strokeText(text, x, y);
ctx.fillText(text, x, y);
ctx.restore();
}
function drawShadowedText(text, x, y, shadowBlur = 3)
{
ctx.save();
ctx.shadowBlur = shadowBlur;
ctx.shadowColor = "#000000";
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
ctx.fillText(text, x, y);
ctx.restore();
}
function drawGlowingText(text, x, y, glowColorHexString, glowDistance = 10)
{
ctx.save();
ctx.shadowBlur = glowDistance;
ctx.shadowColor = glowColorHexString;
ctx.strokeText(text, x, y);
for(let i = 0; i < 3; i++)
ctx.fillText(text, x, y); //seems to be washed out without 3 fills
ctx.restore();
}
function drawBlurredText(text, x, y, blur = 5)
{
//using technique from https://www.html5rocks.com/en/tutorials/canvas/texteffects/
ctx.save();
let width = ctx.measureText(text).width + blur * 2;
ctx.shadowColor = ctx.fillStyle;
ctx.shadowOffsetX = width + x + ctx.canvas.width;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -width + -ctx.canvas.width, y);
ctx.restore();
}
function drawReflectedText(text, x, y, reflectionScale = 0.2, reflectionAlpha = 0.10)
{
ctx.save();
ctx.fillText(text, x, y);
ctx.scale(1, -reflectionScale);
ctx.globalAlpha = reflectionAlpha;
ctx.shadowColor = ctx.fillStyle;
ctx.shadowBlur = 15;
ctx.fillText(text, x, -(y * (1 / reflectionScale)));
ctx.restore();
}
for(let i = 0; i < 4; i++)
{
drawStrokedText ("MVW", 20 + i * 250, 80 * 1);
drawShadowedText ("MVW", 20 + i * 250, 80 * 2, 3);
drawGlowingText ("MVW", 20 + i * 250, 80 * 3, "#FF0000", 10);
drawBlurredText ("MVW", 20 + i * 250, 80 * 4, 5);
drawReflectedText ("MVW", 20 + i * 250, 80 * 5, 0.5, 0.5);
}
<canvas id="myCanvas" width="1000" height="500"></canvas>
Output of the fiddle:
What it supports:
Outline text
Shadow text
Glowing text
Blurred text
Reflected text
Some performance metrics:
Considering using this in a game or at high frame rates?
Check out this jsperf using the above methods.
https://jsperf.com/various-text-effects-html5-2d-context
For a smooth shadow you can try this
ctx.beginPath();
ctx.fillStyle = 'white';
ctx.font = "bold 9pt Tahoma";
ctx.shadowBlur = 3;
ctx.textAlign = "center";
ctx.shadowColor = "#000000";
ctx.shadowOffs = 0;
ctx.fillText('www.ifnotpics.com', 100, 50);
ctx.closePath();