I have a canvas and I want to print words on it in horizontal and vertical orientation. The printing itself works fine but I have a somewhat annoying spacing at the beginning of the words, which becomes extremely visible on the vertical text like the EX on the example image.
I just created a little example which shows what I mean
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// Create a red line in position 150
ctx.strokeStyle = "red";
ctx.moveTo(150, 20);
ctx.lineTo(150, 170);
ctx.stroke();
ctx.font = "50px Arial";
// Show the different textAlign values
ctx.textAlign = "start";
ctx.fillText("EX", 150, 50);
ctx.textAlign = "end";
ctx.fillText("EX", 150, 100);
ctx.textAlign = "center";
ctx.fillText("EX", 150, 150);
<canvas id="myCanvas" width="300" height="200" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
Is there a way to place the beginning of the word at the point I am drawing to or is there a way to calculate the spacing at the beginning and then just subtract it from the starting point of the text?
The TextMetrics interface has actualBoundingBox[...] information that you can use to know by how much the actual bounding box is offset relatively to the textAlign and textBaseline lines.
So adding the measured actualBoundingBoxLeft to your horizontal position when writing LTR text with the "start" text-align will remove this gap.
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
const input = document.querySelector("input");
input.oninput = draw;
draw();
function draw() {
const txt = input.value;
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillStyle = "black";
// Create a red line in position 150
ctx.strokeStyle = "red";
ctx.moveTo(150, 20);
ctx.lineTo(150, 180);
ctx.stroke();
ctx.font = "50px Arial";
ctx.textBaseline = "top"; // needed for the ascent and decsent measurements
// Show the original textAlign
ctx.textAlign = "start";
ctx.fillText(txt, 150, 30);
// fixed
ctx.fillStyle = "green";
const metrics = ctx.measureText(txt);
ctx.fillText(txt, 150 + metrics.actualBoundingBoxLeft, 110);
// Draw the box around our text
ctx.translate( 150, 110 );
const x = 0; // we already did offset by actualBoundingBoxLeft
const y = metrics.actualBoundingBoxAscent * -1;
const width = metrics.actualBoundingBoxRight + metrics.actualBoundingBoxLeft;
const height = metrics.actualBoundingBoxDescent + metrics.actualBoundingBoxAscent;
ctx.strokeRect(x, y, width, height);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
<input value="Éxy"><br>
<canvas id="myCanvas" width="300" height="200" style="border:1px solid #d3d3d3;"></canvas>
Related
In the graphic the idea was to put a shadow only on the white circle, but it looks like the shadow effect was applied to the text, which shouldn't happen.
Canvas:
<canvas class="gradient" id="canvas_gradient_chart" height="70"></canvas>
Javascript:
drawChartGradient(canvas, radian, height, metric) {
const rect = canvas.getBoundingClientRect();
const ctx = canvas.getContext("2d");
canvas.width = rect.width;
canvas.height = height;
ctx.beginPath();
const centerY = canvas.height / 2;
const centerX = this.positionXChart(canvas.width, metric, 20);
ctx.arc(centerX, centerY, radian, 0, Math.PI * 2, false);
ctx.fillStyle ="#ffffff";
ctx.filter = 'drop-shadow(2px 2px 5px rgba(0,0,0,0.6))';
ctx.fill();
ctx.font = 'bold 14pt sans-serif';
ctx.textAlign = 'center';
ctx.strokeStyle ='#ffffff'
ctx.stroke();
ctx.fillStyle ="#622BCF"; // <-- Text colour here
ctx.shadowBlur=0;
ctx.shadowColor='transparent';
ctx.fillText(`${metric ? metric : 0}`, centerX, centerY+8);
//ctx.globalCompositeOperation = 'destination-over';
ctx.globalCompositeOperation = 'source-over';
canvas.addEventListener('mousemove', (e) => {
const relativeCoodinates = {
x:parseInt(`${centerX}`),
y:parseInt(`${centerY}`),
r:radian
}
});
ctx.globalCompositeOperation = 'destination-over';
ctx.save();
ctx.restore();
}
positionXChart(size, number, distance) {
return size/(200 + distance) * (parseInt(`${number}`) + 100 + distance / 2);
}
Canvas would render:
You have to reset the filter or the same will be applied to all elements:
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter#value
See working sample:
const canvas = document.getElementById("canvas")
const ctx = canvas.getContext("2d")
ctx.beginPath()
ctx.arc(50, 50, 40, 0, Math.PI * 2)
ctx.fillStyle = "#ffffff"
ctx.filter = 'drop-shadow(2px 2px 5px)'
ctx.fill()
ctx.beginPath()
ctx.font = 'bold 30pt sans-serif'
ctx.textAlign = 'center'
ctx.textBaseline = "middle"
ctx.fillStyle = "#622BCF" // <-- Text colour here
ctx.filter = 'none'
ctx.fillText("80", 50, 50)
<canvas id="canvas" ></canvas>
And the textBaseline = "middle" I think does a good job at keeping the text centered
I am trying to create below canvas.
Image
my code is below. but I am having trouble to make the canvas look the like the screenshot above. can anyone help me then?
thanks though
<html>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<script>
var canvas;
var canvasContext;
window.onload = function() {
canvas = document.getElementById('gameCanvas');
canvasContext = canvas.getContext('2d');
canvasContext.fillStyle = 'blue';
canvasContext.fillRect(0,0,canvas.width,canvas.height);
canvasContext.fillStyle = 'grey';
canvasContext.fillRect(355,350,120,90);
canvasContext.fillStyle = 'grey';
canvasContext.fillRect(190,350,120,90);
canvasContext.fillStyle = 'grey';
canvasContext.fillRect(520,350,120,90);
canvasContext.fillStyle = 'grey';
canvasContext.fillRect(355,200,120,90);
canvasContext.fillStyle = 'grey';
canvasContext.fillRect(190,200,120,90);
canvasContext.fillStyle = 'grey';
canvasContext.fillRect(520,200,120,90);
}
</script>
</html>
.fillRect creates a filled region of color. However, .rect creates a "shape" that you can then use the .fill() and .stroke() methods upon.
In the below example if converted creation into a loop for brevity
var canvas;
var canvasContext;
window.onload = function() {
canvas = document.getElementById('gameCanvas');
canvasContext = canvas.getContext('2d');
canvasContext.fillStyle = 'blue';
canvasContext.fillRect(0,0,canvas.width,canvas.height);
var height = 90;
var width = 120;
var leftOffset = 190;
var topOffset = 200;
for(var x = 0; x < 6; x++){
canvasContext.beginPath();
canvasContext.rect(leftOffset,topOffset,width,height);
canvasContext.fillStyle = 'grey';
canvasContext.fill();
canvasContext.lineWidth = 4;
canvasContext.strokeStyle = 'lightblue';
canvasContext.stroke();
leftOffset += 165;
if(x === 2){
leftOffset = 190;
topOffset = 350;
}
}
}
JSFIDDLE
This tutorial on HTML5 Canvas rectangles is very handy
To add the text, you would append the following after (or before) the rect creating loop
canvasContext.beginPath();
canvasContext.font = '20pt Arial';
canvasContext.textAlign = 'center';
canvasContext.fillStyle = 'white';
canvasContext.shadowColor = 'black';
canvasContext.shadowOffsetX = 4;
canvasContext.shadowOffsetY = 4;
canvasContext.fillText('CHOOSE A SCENE TO COLOR', canvas.width/2,55);
UPDATED FIDDLE
Tutorials for text align, text shadow, and text.
Try something like this, use a function to draw a rectangle with exactly the border you want. The trick is to use .rect instead of fillRect so that you can use .stroke() immediately after.
<html>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<script>
function draw_bordered_rect(context, x, y, w, h) {
context.rect(x, y, w, h);
context.fillStyle = "grey";
context.fill();
context.lineWidth = 3;
context.strokeStyle = "lightblue";
context.stroke();
}
window.onload = function() {
canvas = document.getElementById('gameCanvas');
canvasContext = canvas.getContext('2d');
canvasContext.fillStyle = 'blue';
canvasContext.fillRect(0, 0, canvas.width, canvas.height);
canvasContext.font = '25pt Arial';
canvasContext.textAlign = 'center';
//drop shadow 2px to the left and 2px below the white text
canvasContext.fillStyle = "black";
canvasContext.fillText('CHOOSE A SCENE TO COLOR', canvas.width/2-2, 82);
//actual text ontop of the drop shadow text
canvasContext.fillStyle = 'white';
canvasContext.fillText('CHOOSE A SCENE TO COLOR', canvas.width/2, 80);
draw_bordered_rect(canvasContext, 355, 350, 120, 90);
draw_bordered_rect(canvasContext, 190, 350, 120, 90);
draw_bordered_rect(canvasContext, 520, 350, 120, 90);
draw_bordered_rect(canvasContext, 355, 200, 120, 90);
draw_bordered_rect(canvasContext, 190, 200, 120, 90);
draw_bordered_rect(canvasContext, 520, 200, 120, 90);
}
</script>
</html>
Looks like:
I have some code to design canvas box in HTML5. I think you should try this one, I hope it will help you to design your canvas box. I think you should use JavaScript mehtod context.fillRect as i am giving you Js Fidler Lind here
HTML Code
<canvas id="myCanvas" width="500" height="400">
<!-- Insert fallback content here -->
</canvas>
JavaScript Code
var canvas = $("#myCanvas");
var context = canvas.get(0).getContext("2d");
// Set rectangle and corner values
var rectX = 50;
var rectY = 50;
var rectWidth = 100;
var rectHeight = 100;
var cornerRadius = 20;
// Reference rectangle without rounding, for size comparison
context.fillRect(200, 50, rectWidth, rectHeight);
// Set faux rounded corners
context.lineJoin = "round";
context.lineWidth = cornerRadius;
// Change origin and dimensions to match true size (a stroke makes the shape a bit larger)
context.strokeRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
context.fillRect(rectX+(cornerRadius/2), rectY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius);
// You can do the same thing with paths, like this triangle
// Remember that a stroke will make the shape a bit larger so you'll need to fiddle with the
// coordinates to get the correct dimensions.
context.beginPath();
context.moveTo(400, 60);
context.lineTo(440, 140);
context.lineTo(360, 140);
context.closePath();
context.stroke();
context.fill();
This javascript code will design canvas box just like below g]iven image
Here is my html
<canvas class="row" id="myCanvas" width="500" height="50" style="border:1px solid #c3c3c3;" ></canvas>
and this is javascript
function showProgress() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillText("welcome", c.width/2, c.height/2);
ctx.textAlign = "center";
ctx.font = "30px Arial";
ctx.fillStyle = "#00ff00";
ctx.fillRect(0, 0, 270, 75);
}
showProgress();
I have following two issues.
1. Green fill rectangle is hiding the text. How can I show that text on top of fill color.
2. I would like text (welcome in this case) color to be red. Is there anyway of modifying just the text color.
code could be found at jsfiddle
https://jsfiddle.net/Lp24q01s/
function showProgress() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillStyle = "#00ff00";
ctx.fillRect(0, 0, 270, 75);
ctx.fillStyle = '#ff0000';
ctx.textAlign = "center";
ctx.font = "30px Arial";
ctx.fillText("welcome", c.width/2, c.height/2);
}
showProgress();
The canvas context uses the entire state to draw things and they're drawn in the order you call them. Want the text on top of a rectangle? Draw the rectangle first. Want to change the color? Set color to green, draw rectangle, set color to red, draw text.
It doesn't have a native way to say "the rectangle is green." It's more "the next thing you draw will have a fill style of green, I think I'll draw a rectangle" so you have a green rectangle.
function showProgress() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillStyle = "#00ff00";
ctx.fillRect(0, 0, 270, 75);
ctx.fillStyle = "#ff0000";// red text
ctx.fillText("welcome", c.width/2, c.height/2);
ctx.textAlign = "center";
ctx.font = "30px Arial";
}
showProgress();
you should put the fillText after the fillRect, also you need to set the fillStyle
because canvas is just a Finite State Machine, it can't remember what you was done.
if you make some difference state(such as change the fillStyle), and do another render(such as fillRect), the current result will always cover the result you done before.
I'm making a game with javascript canvas. I'm drawing all the game elements, like the player, blocks and lines, but I don't want you to see all that. Instead want the whole screen to be black, expect for in some places where there is lightsources. I don't need any shadows, just a circle on the screen that is lit up with a radial gradient. I am able to achieve this for one lightsource by adding a transparent gradient after I have drawn everything else, like this: (imagine the red rectangle to be all the things in the game)
//Drawing all the game elements
ctx.fillStyle = "red";
ctx.fillRect(100, 100, 400, 300);
//adding the darkness and the lightsources
var grd = ctx.createRadialGradient(150, 100, 5, 150, 100, 100);
grd.addColorStop(0, "transparent");
grd.addColorStop(1, "black");
ctx.fillStyle = grd; ctx.fillRect(0, 0, 600, 400);
JSFiddle
But how can I achieve this with multiple lightsources? The technique showed won't work.
I have tried using the Illuminated.js api, but that was incredibly slow, and I don't need anything of the shadows and all that fancy stuff, just a circle where you can see the game.
Here's an example of my approach - create black&white mask and multiply the base with it:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
//Drawing all the game elements
ctx.fillStyle = "red";
ctx.fillRect(100, 100, 400, 300);
//adding the darkness and the lightsources
function addlight(ctx, x, y) {
var grd = ctx.createRadialGradient(x, y, 10, x, y, 150);
grd.addColorStop(0, "white");
grd.addColorStop(1, "transparent");
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 600, 400);
}
var buffer = document.createElement('canvas');
buffer.width = 600;
buffer.height = 400;
b_ctx = buffer.getContext('2d');
b_ctx.fillStyle = "black";
b_ctx.fillRect(0, 0, 600, 400);
addlight(b_ctx, 150, 100);
addlight(b_ctx, 350, 200);
addlight(b_ctx, 450, 250);
ctx.globalCompositeOperation = "multiply";
ctx.drawImage(buffer, 0, 0);
<canvas id="myCanvas" width="600" height="400" style="border:1px solid #d3d3d3;">
Seemed simple enough to draw circles and text in an HTML5 canvas, but I get non-intuitive behavior. The circles get drawn nice and pretty, then the more circles I draw, the older circles become more and more octagonal shaped. Very strange to me... Also, the text disappears from the old circles and only appears on the last circle drawn. What's the proper way to do this with canvases?
$("#circles_container").on("click", "#circles_canvas", function(event) {
var canvas = document.getElementById('circles_canvas');
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
var w = 16;
var x = event.pageX;
var y = Math.floor(event.pageY-$(this).offset().top);
ctx.fillStyle = "rgb(200,0,0)";
ctx.arc(x, y, w/2, 0, 2 * Math.PI, false);
ctx.fill();
ctx = canvas.getContext("2d");
ctx.font = '8pt Calibri';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.fillText('0', x, y+3);
}
});
Just add this near the start of your function :
ctx.beginPath();
You were drawing a path always longer.
Demo in Stack Snippets & JS Fiddle (click on the canvas)
var canvas = document.getElementById('circles_canvas');
canvas.addEventListener("click", function(event) {
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
var w = 16;
var x = Math.floor(event.pageX-this.offsetLeft);
var y = Math.floor(event.pageY-this.offsetTop);
ctx.beginPath();
ctx.fillStyle = "rgb(200,0,0)";
ctx.arc(x, y, w/2, 0, 2 * Math.PI, false);
ctx.fill();
ctx.font = '8pt Calibri';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.fillText('0', x, y+3);
}
})
canvas {
border: 1px solid black;
}
<h1>Click Canvas Below</h1>
<canvas id="circles_canvas"></canvas>