https://jsfiddle.net/diegojsrw/od12s9b4/14/
c.font = "100vh sans-serif";
c.fillStyle = "white";
c.fillText(fps.toFixed(0), w/2, 0);
Initially, the text's height seems fine.
However, when I resize the window, the text doesn't resize together. The text is constantly drawn on canvas (using requestAnimationFrame).
The text size is only re-adjusted when I switch to another tab then switch back to this tab.
Any clues?
You are facing a webkit bug. Both latests Chrome and Safari are concerned.
In the Canvas2D API, relative units/values should get computed at setting and this computed value will get saved and used.
This means that your relative 100vw value will get computed to its corresponding value in absolute px unit.
This is one of the reasons it is advised to always use absolute units from the canvas API (among others like tweaks in roundings etc.).
But if you really want to use this unit, then you have to set it everytime it has changed, so you could go blindly and inside your loop just set ctx.font again before calling fillText(), but for performances, I would advise you use a dirty flag, that would get raised in the window's resize event, and only update the ctx.font property when this flag is raised.
But this is only fine for browsers that do follow the specs...
I have no real clue as to when webkit browsers will compute this value, as even resetting the font property to something else won't do, and even setting it to an other value (e.g switching between 20vh and 21vh) won't do either...
So the only workaround I can see for now, is to actually compute yourself this value. For viewport size, that's not so hard (innerWidth / (100 / vw_val)), but even for other relative units, you can actually set this fontSize on the canvas directly and call getComputedStyle() on the canvas.
let dirtySize = true; // this is our resize flag
const ctx = canvas.getContext('2d');
let fontStyle = ' Arial, sans-serif';
anim(0);
// the animation loop
function anim(time) {
// call the drawing methods
draw(Math.round(time/1e2));
// lwoer the flags
dirtySize = false;
// do it again next frame
requestAnimationFrame(anim);
}
function draw(txt) {
// clear
ctx.clearRect(0,0,canvas.width, canvas.height);
// only if needed
if(dirtySize) {
// get the cpmputed style from our DOM element
const fontSize = getComputedStyle(canvas).fontSize;
// or could be
// const fontSize = (innerWidth / (100 / 20)) + 'px';
ctx.font = fontSize + fontStyle;
}
// draw everytime
ctx.fillText(txt, 0, 100);
}
// on window's resize event
window.addEventListener('resize',
evt => dirtySize = true, // raise our flag
{ passive: true }
);
#canvas {
font-size: 20vw;
}
<canvas id="canvas"></canvas>
Related
Still very much a newbie to coding, so please be gentle :)
I'm hoping someone might be able to help how to use Paper.js on a second canvas after the first one has been executed?
I'm trying to use x2 canvas elements:
Canvas 1 - to capture a html5 video image still and convert to base64 (tick :-) = done)
Canvas 2 - Use the base64 image and perform the 'Working with Rasters to find the colors of pixels' and convert to circle paths (boo = fail :-( )
Something like this:
The code:
<script src="https://cdn.jsdelivr.net/npm/hls.js#latest"></script>
<video id="video" preload="auto" muted="" playsinline="" width="580" src="blob:https://www.georgefisher.co.uk/78e3a45c-ae07-4ea5-af56-45a5ed9cf1b0"></video>
<script>
var video = document.getElementById('video');
var videoSrc = 'https://camsecure.co/HLS/georgefisher.m3u8';
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
}
video.play()
</script>
<br>
<button onclick="capture()">Capture</button>
<br>
<canvas id="canvas" style="overflow:auto">
</canvas>
<canvas id="canvas2" resize>
<img src="" id="myImg"/></canvas>
var resultb64="";
function capture() {
var canvas = document.getElementById('canvas');
var video = document.getElementById('video');
canvas.width = video.videoWidth/4;
canvas.height = video.videoHeight/4;
canvas.getContext('2d').drawImage(video, 0, 0, video.videoWidth/4, video.videoHeight/4);
resultb64=canvas.toDataURL();
document.querySelector("#myImg").src = canvas.toDataURL();
}
/*Paper JS Setup for working in CodePen */
/* ====================== *
* 0. Initiate Canvas *
* ====================== */
// expose paperjs classes into global scope
paper.install(window);
// Only executed our code once the DOM is ready.
window.onload = function() {
// bind paper to the canvas
paper.setup('canvas2');
// paper.activate();
// Get a reference to the canvas object
var canvas = document.getElementById('canvas2');
var ctx = canvas.getContext('2d');
// console.log(ctx, image);
// ctx.drawImage(image, 0, 0);
// return;
// }
// Create a raster item using the image id='' tag
var image = document.querySelector('img');
var raster = new Raster(image);
// Hide the raster:
raster.visible = false;
// The size of our grid cells:
var gridSize = 15;
// Space the cells by 120%:
var spacing = 1
;
// As the web is asynchronous, we need to wait for the raster to load before we can perform any operation on its pixels.
raster.onLoad = function() {
// Since the example image we're using is much too large, and therefore has way too many pixels, lets downsize it to 40 pixels wide and 30 pixels high:
raster.size = new Size(40, 30);
for (var y = 0; y < raster.height; y++) {
for(var x = 0; x < raster.width; x++) {
// Get the color of the pixel:
var color = raster.getPixel(x, y);
// Create a circle shaped path:
var path = new Path.Circle({
center: new Point(x, y).multiply(gridSize),
radius: gridSize / 2 / spacing,
});
// Set the fill color of the path to the color
// of the pixel:
path.fillColor = color;
}
}
// Move the active layer to the center of the view, so all the created paths in it appear centered.
project.activeLayer.position = view.center;
}
}
I've tried giving the second canvas a different Id="canvas2" and referencing that, which I can see in the console. However, nothing appears in the second canvas and the paper.js script doesn't seem to execute, can someone help me understand why?
Please see also see link to the fiddle below:
https://jsfiddle.net/jmnes/o4Lpkfs6/1/
Alternatives method.
You don't need to capture the video, you don't need to capture the pixels using paper.js and raster. You don't need to find the color of each circle and draw it.
All these methods are slow, complex, and power hungry.
You can create a mask and mask out the circles, with the colors drawn from a smaller canvas with a res that matches the number off circles.
How to
Add one (main canvas) canvas to the DOM. This will display the result
Create 2 offscreen canvas.
One (color canvas) has the same resolution as the circles you want to display. Eg if you have 30 by 40 circle the canvas res should be 30 by 40
One (mask canvas) is the circle mask. It is the same resolution as the main canvas. Draw the circles all in one color on this canvas.
Then rendering once a frame
Draw the video on the color canvas to fit.
Turn off smoothing on the main canvas eg ctxMain.imageSmoothingEnabled = false
Draw the color canvas onto the main canvas to fit.
This will draw a color square at each circle position. ctx.drawImage(colorCanvas, 0, 0, mainCanvas.width, mainCanvas.height)
Set composite operation "destination-in" eg ctxMain.globalCompositeOperation = "destination-in"
Draw the mask canvas (canvas with circles on it) onto the main canvas. This will remove pixels outside each circle.
Restore default composite operation for the main canvas ctxMain.globalCompositeOperation = "source-over"
All done for a real-time FX on almost any device.
The above methods is the fastest way to render the effect you are after using the 2D API
I'm working with a function that uses an image to set a div's margin-top.
It finds the image height and the window height and sets the divs margin-top to the lesser of the two values. It works perfectly fine for all local browsers (IE, Chrome, Firefox).
When I upload it to GitHub it fails to recognize the image height however. Now when I run the code below on GitHub the console.log of the image height is 0. When I run it locally the console.log of the image height is correct. The image is loaded on GitHub, so it's not an issue of the image not making into the web page.
It seems like the script runs before the image loads on GitHub, but not locally. Anyone have any ideas?
var portfolioMargin = function() {
var windowHeight = window.innerHeight;
var imageHeight = $('#imageWrap img').height();
//if windowheight is bigger than image height make portfolio div margin equal to image height, else do the opposite
console.log("window height " + windowHeight);
console.log("image height " + imageHeight);
if (windowHeight > imageHeight) {
$('#portfolio').css('margin-top', imageHeight);
} else {
$('#portfolio').css('margin-top', windowHeight);
}
};//end portfolioMargin function
So I figured it out I think. You have to wrap the function in a window.load function to make sure all images have been loaded. From what I understand this won't work for dynamically added images however. Here is the code that fixed it:
//make sure images have loaded before running image height
$(window).load(function() {
portfolioMargin();
});
How can I draw a canvas rectangle with fill color AND 4 different border colors on each side?
You can add a new method to canvas.context that draws your multi-colored rectangle.
You define a new method on the canvas.context through its prototype:
CanvasRenderingContext2D.prototype.myNewMethod = function(){ ... };
Inside the new method, you can use any context drawing commands to draw your desired shape.
Note that inside myNewMethod, “this” refers to the canvas.context, so you draw like this:
this.lineTo(x,y) // not context.lineTo
Your fancy rectangle is a fairly straightforward drawing, except for mitered side strokes.
Each side stroke is drawn as a filled trapezoid:
function trapezoid(context,color,x1,y1,x2,y2,x3,y3,x4,y4){
context.beginPath();
context.moveTo(x1,y1);
context.lineTo(x2,y2);
context.lineTo(x3,y3);
context.lineTo(x4,y4);
context.closePath();
context.fillStyle=color;
context.fill();
}
Your fancy new rectangle method (rainbowRect) is called just like context.fillRect.
context.rainbowRect(100,50,100,50,"gold","red","blue","green","purple");
Here is a complete example:
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// Add a rainbowRect function to the context prototype
// This method is used alone like context.fillRect
// This method is not used within a context.beginPath
// NOTE: this addition must always be run before it is used in code
CanvasRenderingContext2D.prototype.rainbowRect = function (x,y,w,h,fillColor,tColor,rColor,bColor,lColor){
// use existing fillStyle if fillStyle is not supplied
fillColor=fillColor||this.fillStyle;
// use existing strokeStyle if any strokeStyle is not supplied
var ss=this.strokeStyle;
tColor=tColor||ss;
rColor=rColor||ss;
bColor=bColor||ss;
lColor=lColor||ss;
// context will be modified, so save it
this.save();
// miter the lines
this.lineJoin="miter";
// helper function: draws one side's trapezoidal "stroke"
function trapezoid(context,color,x1,y1,x2,y2,x3,y3,x4,y4){
context.beginPath();
context.moveTo(x1,y1);
context.lineTo(x2,y2);
context.lineTo(x3,y3);
context.lineTo(x4,y4);
context.closePath();
context.fillStyle=color;
context.fill();
}
// context lines are always drawn half-in/half-out
// so context.lineWidth/2 is used a lot
var lw=this.lineWidth/2;
// shortcut vars for boundaries
var L=x-lw;
var R=x+lw;
var T=y-lw;
var B=y+lw;
// top
trapezoid(this,tColor, L,T, R+w,T, L+w,B, R,B );
// right
trapezoid(this,rColor, R+w,T, R+w,B+h, L+w,T+h, L+w,B );
// bottom
trapezoid(this,bColor, R+w,B+h, L,B+h, R,T+h, L+w,T+h );
// left
trapezoid(this,lColor, L,B+h, L,T, R,B, R,T+h );
// fill
this.fillStyle=fillColor;
this.fillRect(x,y,w,h);
// be kind -- always rewind (old vhs reference!)
this.restore();
// don't let this path leak
this.beginPath();
// chain
return(this);
};
// testing
ctx.lineWidth=20;
ctx.rainbowRect(100,50,100,50,"gold","red","blue","green","purple");
}); // end $(function(){});
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas" width=300 height=150></canvas>
Unfortunately, you can't draw borders of a rectangle just like you do with css.
You can use stroke() method, but it draws "borders" just with one color for every side.
So, I guess, you can draw borders manually by drawing lines near a rectangle.
Total noob question. I've been abusing Google and for some reason, have surprisingly not been able to find anything regarding this...? I feel like I'm missing something here. :P
I currently have a resize() function that modifies the canvas dimensions to the size of the window. In a minimal example (which also uses jQuery), I have a variable that references my Shape object. According to the documents, the Shape Object does not include a width & height property. What is the most efficient way of resizing a Shape Object? Removing/redrawing dynamically?
This is what I have:
var stage;
var bgColor;
$(document).ready(function(){
init();
});
function init()
{
stage = new createjs.Stage("canvasStage");
bgColor = new createjs.Shape();
bgColor.graphics.beginFill("#000000").drawRect(0,0, stage.canvas.width, stage.canvas.width);
stage.addChild(bgColor);
$(window).resize(function(){windowResize();});
windowResize();
}
function windowResize()
{
stage.canvas.width = $(window).width();
stage.canvas.height = $(window).height();
//bgColor.width = $(window).height();// No width property
//bgColor.height = $(window).height();// NO height property
stage.update();}
You can use the shape's scaleX and scaleY to scale the shape.
Note: The Shape Object extends the DisplayObject so you might also want to look at the DisplayObject docs for many more useful properties/methods.
myShape.scaleX=1.2;
myShape.scaleY=1.2;
as mentioned by #markE, only scaleX and scaleY are available.
A way to work around the problem is to instantiate shapes with 1px width and 1px height:
mc_bg = new createjs.Shape();
mc_bg.graphics.beginFill("#CCCCCC").drawRect(0,0,1,1);
stage.addChild(mc_bg);
mc_left = new createjs.Shape();
mc_left.graphics.beginFill("#333333").drawRect(0,0,1,1);
stage.addChild(mc_left);
mc_circle = new createjs.Shape();
mc_circle.graphics.beginFill("#888888").drawCircle(0,0,1,1);
stage.addChild(mc_circle);
Allowing to set your dimensions with pixel units without conversion directly by using scaleX and scaleY before rendering:
mc_bg.scaleX = stage_width;
mc_bg.scaleY = stage_height;
mc_left.scaleX = stage_width/2;
mc_left.scaleY = stage_height;
mc_circle.x = stage_width/4;
mc_circle.y = stage_width/4;
mc_circle.scaleX = stage_width/4;
mc_circle.scaleY = stage_width/4;
stage.update();
See fiddle example with live resizing on window resize: https://jsfiddle.net/jckleinbourg/go3fs1zt/
Conversion methods to change scale --> dimensions and vice versa:
function dimToScale(origDim, desiredDim)
{
return desiredDim / origDim;
}
function scaleToDim(origDim, scale)
{
return scale * origDim;
}
In image tag, if we don't supply width and height property, we will get nothing when retrieving the width and the height of the image. I am using the canvas element to load an image and scale it proportionally. In order to do this, I have to get the actual image size. Is it possible to do that in html 5?
The HTMLImageElement has two properties for this, naturalWidth and naturalHeight. Use those.
As in:
var img = new Image();
img.addEventListener('load', function() {
// once the image is loaded:
var width = img.naturalWidth; // this will be 300
var height = img.naturalHeight; // this will be 400
someContext.drawImage(img, 0, 0, width, height);
}, false);
img.src = 'http://placekitten.com/300/400'; // grab a 300x400 image from placekitten
It is wise to set source only after the event listener is defined, see Phrogz's exploration on that here: Should setting an image src to data URL be available immediately?
You cannot retrieve width/height before image has been loaded.
Try something like:
// create new Image or get it right from DOM,
// var img = document.getElementById("myImage");
var img = new Image();
img.onload = function() {
// this.width contains image width
// this.height contains image height
}
img.src = "image.png";
Anyhow onload will not fire if image has been loaded before script execution. Eventually you can embed script in html <img src="test.jpg" onload="something()">
if I understand you correctly, you can use the getComputedStyle method.
var object = document.getElementById(el);
var computedHeight = document.defaultView.getComputedStyle(object, "").getPropertyValue("width");
Not fully understood your question.
But you can use javascript to get the width and height of the image.
and then pass it into
/ Five arguments: the element, destination (x,y) coordinates, and destination
// width and height (if you want to resize the source image).
context.drawImage(img_elem, dx, dy, dw, dh);
while drawing image on canvas.
or check this if it helps
http://jsfiddle.net/jjUHs/