I want to show a timelapse video but have it with a Range slider so the user can drag it back and forth. I've tried with a video but I don't like the loading effect as you scroll. I also exported the video as images and binded it to the range slider. First question is, isn't there any plugins or applications out there that do this? Maybe i'm searching the wrong terms. Second is an image sequence the best way to do this? In my code below I can get it to partially work, but I can't figure out how to resize the image, and it's way to big for the canvas.
<style>
canvas {
height: 650px;
width: 1000px;
border: 1px solid black;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<canvas id="canvas" height="650px" width="1000px"></canvas>
<br>
<input id="my-input" type="range" min="1" max="50" value="1">
<script type="text/javascript">
var canvas = document.getElementById("canvas");
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
var totalImages = 50;
var videoFrames = [];
for (var i = 1; i <= totalImages; i++) {
var videoFrame = new Image;
var videoFrameUrl = 'http://dacwebdesign.com/testing/images/timelapse-' + i + '.jpg';
videoFrame.src = videoFrameUrl;
videoFrames.push(videoFrame);
}
$("#my-input").on("input", function(event) {
var currentImage = videoFrames[event.target.value - 1];
var context = canvas.getContext("2d");
context.drawImage(currentImage, 0, 0);
});
</script>
You can resize the images to fit your canvas with:
var currentImage = videoFrames[event.target.value - 1];
var context = canvas.getContext("2d");
var dstWidth = canvas.width;
var dstHeight = canvas.height;
var srcWidth = currentImage.naturalWidth;
var srcHeight = currentImage.naturalHeight;
context.drawImage(currentImage, 0, 0, srcWidth, srcHeight, 0, 0, dstWidth, dstHeight);
That does not take into account aspect ratios, so if your canvas has a different aspect ratio to your images, it will appear distorted.
Related
PS: Is it not a research kind of question! I have been trying to do this from very long time.
I am trying to make web based an image editor where user can select multiple cropping area and after selection save/download all the image area. like below.
As of now I discovered two libraries
1.Cropper.JS where is only single selection feature is available.
2.Jcrop where only single selection area restrictions.
I am currently using cropper.Js but it seems impossible for me to make multiple selection cropping.
Any help is much appreciated.if any other method/library available in JavaScript, Angular or PHP or reactJS for multiple image area selection and crop and download in one go as in the image below.
As per #Keyhan Answer I am Updating my Jcrop library Code
<div style="padding:0 5%;">
<img id="target" src="https://d3o1694hluedf9.cloudfront.net/market-750.jpg">
</div>
<button id="save">Crop it!</button>
<link rel="stylesheet" href="https://unpkg.com/jcrop/dist/jcrop.css">
<script src="https://unpkg.com/jcrop"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
JavaScript
<script>
setImage();
var jcp;
var jcp;
Jcrop.load('target').then(img => {
//You can enable multiple cropping with this line:
jcp = Jcrop.attach(img, { multi: true });
});
// to fix security issue when trying to convert to Data URI
function setImage() {
document.getElementById('target').setAttribute('crossOrigin', 'anonymous');
document.getElementById('target').src = 'https://d3o1694hluedf9.cloudfront.net/market-750.jpg';
}
var link = document.getElementById('save');
link.onclick = function () {
//we check if at least one crop is available
if (jcp.active) {
var i = 0;
var fullImg = document.getElementById("target");
//we are looping cropped areas
for (area of jcp.crops) {
i++;
//creating temp canvas and drawing cropped area on it
canvas = document.createElement("canvas");
canvas.setAttribute('width', area.pos.w);
canvas.setAttribute('height', area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(fullImg, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
//creating temp link for saving/serving new image
temp = document.createElement('a');
temp.setAttribute('download', 'area' + i + '.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
</script>
I tried to explain the code with comments:
var jcp;
Jcrop.load('target').then(img => {
//You can enable multiple cropping with this line:
jcp = Jcrop.attach(img,{multi:true});
});
//assuming you have a button with id="save" for exporting cropped areas
var link=document.getElementById('save');
link.onclick = function(){
//we check if at least one crop is available
if(jcp.active){
var i=0;
var fullImg = document.getElementById("target");
//we are looping cropped areas
for(area of jcp.crops){
i++;
//creating temp canvas and drawing cropped area on it
canvas = document.createElement("canvas");
canvas.setAttribute('width',area.pos.w);
canvas.setAttribute('height',area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(fullImg, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
//creating temp link for saving/serving new image
temp = document.createElement('a');
temp.setAttribute('download', 'area'+i+'.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
EDIT: As you commented it would be nicer if we have local image loader, we can add a file input to our html
<img id="target" />
<br/>
<input type="file" id="imageLoader" name="imageLoader"/><!-- add this for file picker -->
<button id="save">save</button>
and a function to our js to handle it
var jcp;
var save=document.getElementById('save');
var imageLoader = document.getElementById('imageLoader');
var img = document.getElementById("target");
imageLoader.onchange=function handleImage(e){//handling our image picker <input>:
var reader = new FileReader();
reader.onload = function(event){
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
save.onclick = function(){
if(jcp&&jcp.active){
var i=0;
for(area of jcp.crops){
i++;
canvas = document.createElement("canvas");
canvas.setAttribute('width',area.pos.w);
canvas.setAttribute('height',area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(img, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
temp = document.createElement('a');
temp.setAttribute('download', 'area'+i+'.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
Jcrop.load('target').then(img => {
jcp = Jcrop.attach(img,{multi:true});
});
Yes, #keyhan was right <input type="file"> is another question, but still, I am giving you an idea of how to implement Kayhan's code above.
<div>
<input type="file" id="image-input" accept="image/*">
<!-- img id name should be "target" as it is also using by Jcrop -->
<img id="target"></img>
</div>
and Now you can put below JavaScript Code just above setImage()
<script>
let imgInput = document.getElementById('image-input');
imgInput.addEventListener('change', function (e) {
if (e.target.files) {
let imageFile = e.target.files[0];
var reader = new FileReader();
reader.onload = function (e) {
var img = document.createElement("img");
img.onload = function (event) {
var MAX_WIDTH = 1600;
var MAX_HEIGHT = 800;
var width = img.width;
var height = img.height;
// Change the resizing logic
if (width > height) {
if (width > MAX_WIDTH) {
height = height * (MAX_WIDTH / width);
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width = width * (MAX_HEIGHT / height);
height = MAX_HEIGHT;
}
}
// Dynamically create a canvas element
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
// var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// Actual resizing
ctx.drawImage(img, 0, 0, width, height);
// Show resized image in preview element
var dataurl = canvas.toDataURL(imageFile.type);
document.getElementById("target").src = dataurl;
}
img.src = e.target.result;
}
reader.readAsDataURL(imageFile);
}
});
</script>
Main Issue:
l essentially want to figure the issue with my event listener as it is aligning with the canvas object, which is the image '', in the middle of the canvas however, the Y areas below it are still clickable and the X areas on the right of it are still clickable.
l would like to eliminate this issue, which l believe is being caused by my IF statement and the DRAWIMAGE conditions, in relation to my canvas. There is a reproducible demo, fullscreen/expand it to see.
Another issue:
Another thing to note, which would be much appreciated, is the canvas object not truly sticking in one position on the canvas when you resize the browser window. It simply moves off into a different direction even though it should be stuck in one area of the canvas no matter what size my browser's window is - meaning that the canvas object somehow needs to dynamically resize along with how my browser resize + the event listener needs to see it. Again this would be highly appreciated as l really want to understand the error of my logic as l might using the wrong coordinate system,i don't really know :/
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
var the_button = document.getElementById("the_button");
var the_background = document.getElementById("the_background");
var button_imageX = 600;
var button_imageY = 390;
window.onload = function() {
ctx.drawImage(the_background, 0, 0, canvas.width, canvas.height);
drawButton();
}
initialize();
function initialize() {
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
}
function drawButton() {
/* l belive this is partly responsible aswell for the issue */
ctx.drawImage(the_button, button_imageX, canvas.height - button_imageY, 170, 100);
}
function redraw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(the_background, 0, 0, canvas.width, canvas.height);
drawButton();
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
redraw();
}
the_button_function = (paramater1) => {
/* Problem lies here in the IF statement aswell, that's my guess */
if ((paramater1.x > (canvas.width - button_imageX)) && (paramater1.x < canvas.width) && (paramater1.y > (canvas.height - button_imageY)) && (paramater1.y < canvas.height)) {
alert("<Button>")
}
}
canvas.addEventListener('click', (e) => the_button_function(e));
html,
body {
width: 100%;
height: 100%;
margin: 0px;
border: 0;
overflow: hidden;
display: block;
}
<html>
<canvas id="c"></canvas>
<img style="display: none;" id="the_button" src="https://i.imgur.com/wO7Wc2w.png" />
<img style="display: none;" id="the_background" src="https://img.freepik.com/free-photo/hand-painted-watercolor-background-with-sky-clouds-shape_24972-1095.jpg?size=626&ext=jpg" />
</html>
To stop clicks responding to the right of and below the button, take into account the width and height of the button!
Fixing this, and knowing where the image was previously drawn on the canvas should fix the second issue. To debug the problem the snippet code below replaces
button_imageX with button_imageLeft - how many pixels from canvas left to draw the image.
button_imageY with button_imageBottom - how many pixels from canvas bottom to draw the top of the image. (This seemed to be how the posted code was positioning the button in the y direction.)
[image_bottonLeft and image_buttonBottom values were modified for seeing results on Stack Overflow.]
And introduced
button_offsetX - x position of where the left hand side of the button was last drawn
button_offsetY - y position of where the top of the button was last drawn
button_imageWidth and button_imageHeight values for button height and width, replacing hard coded values function drawButton.
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
var the_button = document.getElementById("the_button");
var the_background = document.getElementById("the_background");
var button_imageLeft = 100;
var button_imageBottom = 150;
var button_imageWidth = 170;
var button_imageHeight = 100;
var button_offsetX, button_offsetY;
window.onload = function() {
ctx.drawImage(the_background, 0, 0, canvas.width, canvas.height);
drawButton();
}
initialize();
function initialize() {
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
}
function drawButton() {
button_offsetX = button_imageLeft;
button_offsetY = canvas.height - button_imageBottom;
ctx.drawImage(the_button, button_offsetX, button_offsetY, button_imageWidth, button_imageHeight);
}
function redraw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(the_background, 0, 0, canvas.width, canvas.height);
drawButton();
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
redraw();
}
the_button_function = (event) => {
const {x,y} = event;
if( (x >= button_offsetX && x < (button_offsetX+button_imageWidth))
&& (y >= button_offsetY && y < (button_offsetY+button_imageHeight))) {
alert("<Button>")
}
}
canvas.addEventListener('click', (e) => the_button_function(e));
html,
body {
width: 100%;
height: 100%;
margin: 0px;
border: 0;
overflow: hidden;
display: block;
}
<canvas id="c"></canvas>
<img style="display: none;" id="the_button" src="https://i.imgur.com/wO7Wc2w.png" />
<img style="display: none;" id="the_background" src="https://img.freepik.com/free-photo/hand-painted-watercolor-background-with-sky-clouds-shape_24972-1095.jpg?size=626&ext=jpg" />
We currently upload an image via the below code:
reader.onload = function (event) {
fabric.Image.fromURL(event.target.result, function (img) {
whiteboardService.uploadImageToCanvas(img);
});
}
and...
service.uploadImageToCanvas = function (image) {
service.canvas.add(image);
image.id = service.getUniqueId();
service.objectMap[image.id] = image;
var data = {
image: image,
id: image.id
};
signalService.sendMessage(service.recipient, data);
};
If the image is too large, bigger than our fixed width and height of the canvas would it be possible for that image to be scaled down automatically to fit into the fixed width and height of the canvas?
I should point out that I am using Angular.js as well
ta
fabricjs put a very simple method to do this.
Those methods are scaleToWidth and scaleToHeight that are close to 'apply a scale factor that will fit the image on the specified dimensions'
So you can do
image.scaleToWidth(service.canvas.getWidth());
service.canvas.add(image);
it depends if you want to preserve aspect ratio if you have to scale for the biggest, the smallest or both.
Inspired by #AndreaBogazzi, I wrote below code to fit image to canvas totally.
var canvas = new fabric.Canvas('canvas');
fabric.Image.fromURL('https://d32ogoqmya1dw8.cloudfront.net/images/NAGTWorkshops/structure/SGT2012/activities/noaa_global_topographic_map.jpg', function(oImg) {
let imgWidth = oImg.width;
let imgHeight = oImg.height;
let canvasWidth = canvas.getWidth();
let canvasHeight = canvas.getHeight();
let imgRatio = imgWidth / imgHeight;
let canvasRatio = canvasWidth / canvasHeight;
if(imgRatio <= canvasRatio){
if(imgHeight> canvasHeight){
oImg.scaleToHeight(canvasHeight);
}
}else{
if(imgWidth> canvasWidth){
oImg.scaleToWidth(canvasWidth);
}
}
canvas.clear();
canvas.add(oImg);
canvas.centerObject(oImg);
});
.image-preview{
border: solid 1px #979797;
width: 200px;
height: 200px;
}
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>
<div class="image-preview">
<canvas id="canvas" width='200' height='200'></canvas>
<hr/>
<p>Origin Image</p>
<img src="https://d32ogoqmya1dw8.cloudfront.net/images/NAGTWorkshops/structure/SGT2012/activities/noaa_global_topographic_map.jpg" />
</div>
I need to get the color of any pixel my mousepointer is currently hovering.
I found several solutions for canvas elements, but none for a background image defined in CSS for a element.
How can I achieve this?
Combining the answers from Get a CSS value with JavaScript and How to get a pixel's x,y coordinate color from an image?, I was able to simulate what you are looking for: JSFiddle
<html>
<head>
<style type="text/css">
#test {
background-image: url('http://jsfiddle.net/img/logo.png');
background-color: blue;
background-repeat: no-repeat;
height: 30px;
width: 100%;
}
#hidden {
display: none;
}
</style>
<script type="text/javascript">
var div = document.getElementById("test");
var style = window.getComputedStyle(div);
div.onmousemove = function(e) {
var path = style.getPropertyValue('background-image');
path = path.substring(4, path.length-1);
var img = document.getElementById("i1");
img.src = path;
var canvas = document.getElementById("c1");
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var pixelData = canvas.getContext('2d').getImageData(event.offsetX, event.offsetY, 1, 1).data;
var values = document.getElementById("values");
values.innerHTML = 'R: ' + pixelData[0] + '<br>G: ' + pixelData[1] + '<br>B: ' + pixelData[2] + '<br>A: ' + pixelData[3];
};
</script>
</head>
<body>
<div id="test"></div>
<div id="hidden">
<img id="i1" src="" />
<canvas id="c1"></canvas>
</div>
<div id="values"></div>
</body>
</html>
I retrieved the computed style (var style = window.getComputedStyle(div);) outside of the mouse move function for performance reasons, but if the background image is going to change dynamically, then it may need to be moved into the function.
There are some possible browser constraints for using getComputedStyle.
SCALING
You could try editing the code to adjust for the scale:
var h = parseInt(style.getPropertyValue("width")),
w = parseInt(style.getPropertyValue("height"));
var img = document.getElementById("i1");
img.src = path;
var canvas = document.getElementById("c1");
canvas.width = h;
canvas.height = w;
canvas.getContext('2d').drawImage(img, 0, 0, w, h);
This also includes a change to the CSS: JSFiddle
I have a canvas like this
<canvas id="canvas" width="600px" height="300px"></canvas>
Now I am trying to show image inside the canvas of some 260px width and height. But its showing the image full screened in canvas. What I am doing wrong?
img.onload = function(){
var im = this;
var imgWidth = im.width;
var imgHeight = im.height;
var imgScaledWidth = 0;
var imgScaledHeight = 0;
imgScaledHeight = self.conHeight - 40;
imgScaledWidth = imgScaledHeight * (imgWidth/imgHeight);
self.context.drawImage(this, 0,0,imgScaledWidth,imgScaledHeight);
}
Its showing like this
There's no reason why your code shouldn't be working based on what you've shown in the question. Here is an example based on the code you've given with some minor changes to account for not having a self variable.
<script>
var img = document.getElementById("image");
var c = document.getElementById("canvas");
var con = c.getContext("2d");
img.onload = function(){
var im = this;
var imgWidth = im.width;
var imgHeight = im.height;
var imgScaledWidth = 0;
var imgScaledHeight = 0;
var off=20;
imgScaledHeight = c.height - off;
imgScaledWidth = imgScaledHeight * (imgWidth/imgHeight);
con.drawImage(this, 0,0+(off/2),imgScaledWidth,imgScaledHeight);
}
</script>
You are stretching it. If you don't want to show the image stretched, just don't pass any size to drawImage:
self.context.drawImage(this, 0, 0);