I am trying to resize the canvas width to a given pixel amount like this:
if (condition==true) {
canvasVar.width = pixel amount;
}
The if statement is executing, but the canvas is resizing weird. I've tried a number of methods:
var canvasVar = document.getElementById('canvasId');
var contextVar = canvasVar.getContext('2d');
//all numbers in pixels.
canvasVar.style.width = 20; //doesn't do anything.
canvasVar.width = 20; //resizes the HEIGHT!!!
contextVar.canvas.width = 20; //also resizes height.
The canvas width/height values after method 2 & 3 are appiled are 656 (normal value) & 1405 (normal value = 328).
NOTE: I am setting canvas default width/height with CSS and I am also using the Yii PHP framework.
Thanks.
EDIT: I've also tried to follow 20 by the px extension. Gives an error saying 'unexpected identifier'.
A canvas has "2" height and width properties. One of them specifiesthe resolution of the canvas, the other the size
canvasVar[0].width = 20; // canvas consists of 20 virtual px in width
canvasVar[0].style.width = '400 px' // canvas gets a width of 400 px
Related
I have a video that's streaming parts of a video into a MediaSource and playing it. I have a canvas that I want to draw the video on an then overlay some effects. However, in my handler, the call to drawImage, while it works, it only appears to copy a single pixel. The video itself plays fine.
I have a screenshot from my app, which has the source video next to the destination canvas element:
As you can sorta see, the solid color of the canvas is the color of the very first pixel of the source video.
The actual code in question looks similar to this:
startWatch() {
this.timer = setInterval(this.copyFrame.bind(this), 1000 / this.fps)
}
copyFrame() {
const { width, height } = this.video.getBoundingClientRect()
// I've tried the 3, 5, and 9 argument versions of this function
// width is 300 and height is 168.75.
this.ctx.drawImage(this.video, 0, 0, width, height, 0, 0, width, height)
}
(where this.video is a video element)
This code does "work" in that the canvas's color does update over time as the video plays. But it's always just the initial pixel. So the timing loop works, but the drawImage call isn't doing what I expect.
(1) Using const { width, height } may not be correct. Check the values you are given.
What happens if you console.log the width or height values, are they correct/expected numbers?
(2) "...But the drawImage call isn't doing what I expect."
ctx.drawImage( someElement, 0, 0, A1, A2, 0, 0, B1, B2 );
Consider the above code,
The first width/height (A1/A2) is for how much of the input to draw (it must match size of input's own width/height, otherwise cropping or margins will occur). Eg: A1 == 720 if input video is 720 pixels wide.
The second width/height (B1/B2) is for how much resize of the output to draw. Usually you want to fill entire canvas area so use canvas dimensions (aspect ratio is left to coder to implement as preferred).
In your shown code, your const { width, height } is either
Giving too small a number for A1/A2 params (it samples one pixel)
Giving too large a number for B1/B2 params (it stretches output so large that one pixel fills the canvas).
Possible solutions:
Try (if you must use bounding rectangle):
function copyFrame()
{
//# width is 300 and height is 168.75.
//const { width, height } = this.video.getBoundingClientRect()
let myRect = this.video.getBoundingClientRect();
this.ctx.drawImage( this.video, 0, 0,
this.video.videoWidth, this.video.videoHeight,
0, 0, myRect.width, myRect.height);
}
Alternativey you could just draw the picture into canvas size:
function copyFrame()
{
//# assuming your Canvas is called "this.cnvs"
this.ctx.drawImage( this.video, 0, 0,
this.video.videoWidth, this.video.videoHeight,
0, 0, this.cnvs.width, this.cnvs.height);
}
For aspect ratio you can use the below formula (swap all width & height references for a new drawn_width)
myRatio = ( vid_height / vid_width );
drawn_height = ( myCanvas_width * myRatio );
With the code below I want to end up with:
a 400x400 camera capture
a 400x400 canvas
a 400x400 image
let capture
let universalWidth = 400
let testImg = undefined
function setup() {
testImg = document.getElementById('test')
capture = createCapture({
audio: false,
video: {
width: universalWidth,
height: universalWidth
}
})
capture.size(universalWidth, universalWidth)
createCanvas(universalWidth, universalWidth)
}
function draw() {
image(capture.get(), 0, 0)
testImg.src = canvas.toDataURL()
}
Instead, what I end up with is:
a 400x400 camera capture
a 400x400 canvas with its width and height attributes set to 800
a 800x800 image
How do I fix this ?
You can see the problem in action here
The canvas's width and height properties are set to 800 because you are running this sketch on a high DPI (i.e. Apple Retina) display. When p5.js detects this it sets pixel density to 2 by default so it's taking 800 pixel square of graphics and squishing it into a 400 "pixel" square on the screen. If you want an image that is exactly 400 actual pixels, you could set pixel density to 1 in your setup() function: pixelDensity(1).
So I have a program that creates images with a variable amount of DPI. I.e. the user can specify the width and height in inches and then the DPI, that is to say something like this:
save_image_as_png(
image,
height = "9 inches",
width = "6 inches",
dpi = 300
)
My issue is then how to preserve these dimensions when trying to display this image in a html document. My understanding is that html sites print images with a fixed 72 DPI thus raising the DPI even whilst keeping the height and width the same will increase the image size in a html document. Is it possible to use some form of CSS to force the image to be displayed with the source DPI(in this case 300) ? i.e. if 2 images were created as being 9 inches wide and 6 inches tall can I get them to be displayed as the same size regardless of their source DPI?
I've managed to hack together a solution in Javascript where the user provides a scaling constant (i.e. what multiple of 72 was the source image created with) as a data attribute, however it feels wrong and was wondering if there was a proper solution in CSS ?
For reference my hack JS solution:
Html
<img src="my_image.png" class="scaleme" data-scale="5" />
JS
$(window).ready(fix_image_dim);
$(window).resize(fix_image_dim);
function fix_image_dim() {
var imgs = $(".scaleme");
imgs.each(function(index){
var x = imgs[index];
var scale = $(x).data("scale") || 4;
var ratio = x.naturalHeight / x.naturalWidth;
var maxwidth = x.naturalWidth / scale;
if( x.width >= maxwidth){
$(x).css({ 'width' : ''});
x.width = maxwidth;
} else {
$(x).css({ 'width' : '100%'});
}
x.height = x.width * ratio;
})
}
From what I can tell, JCrop will not let me set things up so the user can crop outside the actual image and include surrounding whitespace. Is there a way to do that?
To help explain what I mean, say we are restricting our crop to a 16:9 ratio. That works fine for an image with a naturally wide subject:
But sometimes the source image that a user wants to use does not comfortably accommodate the desired ratio:
Instead, we'd like to allow them to include space outside the image by making the crop area larger than the image itself:
I've been messing around with JCrop and looking through the manual and Google for a while and it doesn't look like this is possible (without modifying JCrop). Am I wrong? If so, how do you do it?
FWIW, the actual images in this case will be product/organization logo images, which come in a large variety of aspect ratios, and almost always the images available to people have almost no whitespace around the text/imagery. Which means any fixed aspect ratio crop restricted to the bounds of the image will almost certainly chop off either the top+bottom or left+right sides of the image.
My solution was to create a temporary canvas with square dimensions equal to the largest side of the image. I made the canvas background white and added the image in the center. Then I created a new image and used the canvas as the image source. Then I used that image with jcrop. It's slower, but it works!
Here's an example:
img.onload = function(){
// get the largest side of the image
// and set the x and y coordinates for where the image will go in the canvas
if( img.width > img.height ){
var largestDim = img.width;
var x = 0;
var y = (img.width-img.height)/2;
}
else{
var largestDim = img.height;
var y = 0;
var x = (img.height-img.width)/2;
}
// create a temporary canvas element and set its height and width to largestDim
canvastemp = document.createElement("canvas");
canvastemp.width = canvastemp.height = largestDim;
var ctx = canvastemp.getContext('2d');
// set the canvas background to white
ctx.fillStyle="#FFFFFF";
ctx.fillRect(0, 0, canvastemp.width, canvastemp.height);
// center the image in the canvas
ctx.drawImage(img, x, y, img.width, img.height);
// create a new image and use the canvas as its source
var squaredImg = document.createElement("img");
squaredImg.src = canvastemp.toDataURL();
// add jcrop once the image loads
squaredImg.onload = function(){
addJcrop(squaredImg);
}
};
function addJcrop(img){
// your jcrop code
}
This way users can choose to include the entire image in the crop if they wish.
consider using something like php imagick to convert the photo to photo + transparent big background and then put that to JCrop I dont think its possible other way
You could fool the jCrop script. Instead of showing image.jpg, you do something like not_a_real_image.php?imagename=image.jpg.
Then give the php file a header of the image, and a width and height, and align the actual image in the center of that.
All you have to do is remember the amount of canvas you've added to correct it later on.
I made a function using Imagick:
function resizeImage($imgSrc, $width, $height, $createBg, $output, $show) {
$img = new Imagick($imgSrc);
if ($img->getImageWidth() / $img->getImageHeight() < $width / $height) {
$img->thumbnailImage(0, $height);
} else {
$img->thumbnailImage($width, 0);
}
$canvas = new Imagick();
$canvas->newImage($width, $height, 'white', 'jpg');
/* Creates a background image (good for vertical images in horizontal canvas or vice-versa) */
if ($createBg) {
$imgBg = new Imagick($imgSrc);
if ($imgBg->getImageWidth() / $imgBg->getImageHeight() < $width / $height) {
$imgBg->thumbnailImage($width, 0);
} else {
$imgBg->thumbnailImage(0, $height);
}
$imgBg->blurImage(0, 80);
$geometryBg = $imgBg->getImageGeometry();
$xBg = ( $width - $geometryBg['width'] ) / 2;
$yBg = ( $height - $geometryBg['height'] ) / 2;
$canvas->compositeImage( $imgBg, imagick::COMPOSITE_OVER, $xBg, $yBg );
}
/* Center image */
$geometry = $img->getImageGeometry();
$x = ( $width - $geometry['width'] ) / 2;
$y = ( $height - $geometry['height'] ) / 2;
$canvas->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
/* Save image */
if ($output) {
$canvas->writeImage($output);
}
/* Show the image */
if ($show) {
header( 'Content-Type: image/jpg' );
echo $canvas;
}
}
The comment's explain it all, enjoy!
How can I dynamically set the image size ratio depending on the returned image in raphael?
Here is some code to give you an idea:
var viewer = Raphael(0,0,scrWidth, scrHeight);
viewer.image(dynamicUrl, 140, 140,300, scaledHeight);
Thank you
You can load the image outside the DOM and get its dimensions... You can put this inside a function:
var myImg = new Image();
myImg.src = dynamicUrl;
myImg.onload = function() {
var width = myImg.width;
var height = myImg.height;
var scale = 0.5; // for example
var viewer = Raphael(0,0,width, height); // or whatever other size
viewer.image(dynamicUrl, 0, 0, width*scale, height*scale); // scale image
// after the image is in the viewer you can use .scale()
}
jsFiddle
Now you can divide or multiply both width and height to scale. Make sure you pay attention to the timing.
Also, once the image is in Raphael, you can use .scale()