I want to make an apple-style page where there's a canvas displaying an image sequence on scroll, but that scrolling doesn't move the page until the last image of the sequence. How can I do this? This is what I've got.
html:
<body>
<canvas id="image-hero"></canvas>
<h1 class="centered title" style="font-size: 4rem;">Test Text</h1>
</body>
js:
const html = document.documentElement;
const canvas = document.getElementById('image-hero');
const context = canvas.getContext('2d');
const frameCount = 148;
const currentFrame = index => ( `https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero-lightpass/${index.toString().padStart(4, '0')}.jpg` )
const preloadImages = () => {
for (let i = 1; i < frameCount; i++) {
const img = new Image();
img.src = currentFrame(i);
}
};
const img = new Image()
img.src = currentFrame(1);
canvas.width = 1158;
canvas.height = 770;
img.onload = function() { context.drawImage(img, 0, 0); }
const updateImage = index => {
img.src = currentFrame(index);
context.drawImage(img, 0, 0);
}
window.addEventListener('scroll', () => {
//don't scroll the page until the animation is done
const scrollTop = html.scrollTop;
const maxScrollTop = html.scrollHeight - window.innerHeight;
const scrollFraction = scrollTop / maxScrollTop;
const frameIndex = Math.min(
frameCount - 1,
Math.ceil(scrollFraction * frameCount)
);
requestAnimationFrame(() => updateImage(frameIndex + 1))
});
preloadImages()
disableScrolling();
thank you!
The best way imo would be to make the positioned fixed, so that when you scroll the canvas itself will stay in the same play, so the content on it won't move.
You can set this directly in the HTML like
canvas id="image-hero" style="position: fixed; top: 100px; left: 0px" ></canvas>
This should make it so that the canvas is positioned at 100px from the top and 0px from the left, and doesn't move when the page is scrolled.
alternatively, you can change the attributed from the JS so that you can control when you want to make the canvas scroll and not scroll
let el = document.getElementById("image-hero");
el.style.position = "fixed"; // this will make the canvas not move
el.style.position = "relative"; // this will make the canvas reset to scrolling with the page
hope this helps
Related
I'm just starting JavaScript and still struggling a lot to reach my goals. I managed to make this little script work, which allows me to start a frame by frame animation related to the scroll of the page. My animation is a loop and I would like from now on simply that arrived at the bottom of the page the scroll restart automatically at the top of the page. So that the animation seems infinite.
I have seen some solutions with a clone of my content but it does not seem very suitable. Here is the JS as is.
Thanks in advance for your ideas!
const html = document.documentElement;
const canvas = document.getElementById("fuck");
const context = canvas.getContext("2d");
const frameCount = 100;
const currentFrame = index => (`https://www.jabberwock.archi/maquette/img/${index.toString().padStart(4, '0')}.jpg`)
const preloadImages = () => {
for (let i = 1; i < frameCount; i++) {
const img = new Image();
img.src = currentFrame(i);
}
};
const img = new Image()
img.src = currentFrame(1);
canvas.width=1800;
canvas.height=1206;
img.onload=function(){
context.drawImage(img, 0, 0);
}
const updateImage = index => {
img.src = currentFrame(index);
context.drawImage(img, 0, 0);
}
window.addEventListener('scroll', () => {
const scrollTop = html.scrollTop;
const maxScrollTop = html.scrollHeight - window.innerHeight;
const scrollFraction = scrollTop / maxScrollTop;
const frameIndex = Math.min(
frameCount - 1,
Math.ceil(scrollFraction * frameCount)
);
requestAnimationFrame(() => updateImage(frameIndex + 1))
});
preloadImages()
and the link of the page to see the animation :
https://jabberwock.archi/maquette/
Thank a lot !
My goal is to have the user upload a local image. I draw this image in a canvas element. On top of this canvas element, I create another canvas, which I use to draw boxes on, so that the boxes overlay the uploaded image.
I want the image to be uploaded in a specific size, say maximum 100 width and maximum 100 height. It's not enough for me to have the image merely displayed at max-height and max-width: 100, it physically needs to be resized during the upload process. Because if I put a super big picture in a say 100x100 canvas, then the boxes on top of it become very small and I need them to be the same size regardless what size the image in the canvas is.
Below is the code:
HTML:
<div style="position: relative;">
<canvas id="can"
style="position: absolute; left: 0; top: 0; z-index: 0;max-width:80%;"></canvas>
<canvas id="box"
style="position: absolute; left: 0; top: 0; z-index: 1;max-width:80%;"></canvas>
</div>
<input type="file" multiple="false" accept="image/*" id="finput" onchange="upload()">
And JS:
function upload() {
//Get input from file input
var fileinput = document.getElementById("finput");
//Make new SimpleImage from file input
image = new SimpleImage(fileinput);
//Get canvas
var canvas = document.getElementById("can");
//Draw image on canvas
image.drawTo(canvas);
}
I use the simpleImage library because it lets me extract RGB values of the image.
let originalWidth;
let originalHeight;
let imageWidth;
let imageHeight;
const load = result => {
return new Promise((fulfill, _reject) => {
let imageObj = new Image();
imageObj.onload = () => fulfill(imageObj);
imageObj.src = result;
});
}
const upload = () => {
const fileinput = document.getElementById("finput");
const canvas = document.getElementById("can");
let context = canvas.getContext("2d");
const reader = new FileReader();
reader.onload = event => {
Promise.all([
load(event.target.result)
])
.then(images => {
originalWidth = images[0].width;
originalHeight = images[0].height;
imageWidth = originalWidth;
imageHeight = originalHeight;
imageWidth = 200; // Fixed value
// Value proportional to width. Keeping to scale without distorting the image ( recommended )
imageHeight = originalHeight * (imageWidth / originalWidth);
context.drawImage(images[0], positionX, positionY, imageWidth, imageHeight);
})
}
reader.readAsDataURL(fileinput);
}
l believe to have a logic error in the way of which my logic is meant to find the previous coordinates of my canvas object, a moving image, and delete the frame drawn before it so that it does not duplicated the image every time it is drawn onto the canvas.
There is a reproducible demo below and l added comments where l believe the problem occurs.
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
var the_background = document.getElementById("the_background");
var imgTag = new Image();
var X_POS = canvas.width;
var Y_POS = 0;
imgTag.src = "http://i.stack.imgur.com/Rk0DW.png"; // load image
function animate() {
/* Error resides from here */
var coords = {};
coords.x = Math.floor(this.X_POS - imgTag);
coords.y = Math.floor(this.Y_POS - imgTag);
ctx.clearRect(coords.x, coords.y, X_POS, Y_POS);
/* To here */
ctx.drawImage(imgTag, X_POS, Y_POS);
X_POS -= 5;
if (X_POS > 200) requestAnimationFrame(animate)
}
window.onload = function() {
ctx.drawImage(the_background, 0, 0, canvas.width, canvas.height);
animate();
}
html,
body {
width: 100%;
height: 100%;
margin: 0px;
border: 0;
overflow: hidden;
display: block;
}
<html>
<canvas id="c" width="600" height="400"></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>
It seems logical to only clear the subset of the canvas that's changing, but the normal approach is to clear and redraw the entire canvas per frame. After the car moves, the background that's cleared underneath it needs to be filled in, and trying to redraw only that subset will lead to visual artifacts and general fussiness. Clearing small portions of the screen is a premature optimization.
Canvases can't keep track of much of anything other than pixels, so an animation is more like a flipbook of stationary frames and less like a cardboard cutout animation, where the same pieces of cardboard move along and overlap one another.
Math.floor(this.X_POS - imgTag) looks wrong -- imgTag is an image object, which doesn't make sense to subtract from a number. You may have meant to grab the x property from this.
Use image.onload to ensure the image is actually loaded before running the loop. It's a bit odd to use image tags in the DOM just to load images for a canvas. I'd do that programmatically from JS which saves some typing and makes it easier to manage the data.
const loadImg = url => new Promise((resolve, reject) => {
const img = new Image();
img.onerror = reject;
img.onload = () => resolve(img);
img.src = url;
});
const images = [
"https://img.freepik.com/free-photo/hand-painted-watercolor-background-with-sky-clouds-shape_24972-1095.jpg?size=626&ext=jpg",
"http://i.stack.imgur.com/Rk0DW.png",
];
Promise
.all(images.map(loadImg))
.then(([backgroundImg, carImg]) => {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const car = {x: canvas.width, y: 0};
(function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(
backgroundImg, 0, 0, canvas.width, canvas.height
);
ctx.drawImage(
carImg, car.x, car.y, carImg.width, carImg.height
);
car.x -= 5;
if (car.x > 200) {
requestAnimationFrame(update);
}
})();
})
.catch(err => console.error(err))
;
<canvas id="canvas" width="600" height="400"></canvas>
I have a function that draw a CANVAS line and make this get the same coordinates of a <div> by using offsetLeft and move its searching the same position of the <div>. It is working good.
drawCanvas() {
const c = document.getElementById("canvas");
const lineH = c.getContext("2d");
c.width = window.innerWidth;
c.height = window.innerHeight;
const positionCanvas = () => {
const divPosition = document.querySelector('.myDiv').offsetLeft
lineV.fillStyle = "grey";
lineV.fillRect(divPosition , 0, 2, window.innerHeight);
lineV.fill();
}
positionCanvas()
window.onresize = () => {
lineV.height = window.innerHeight;
positionCanvas()
}
The problem is I don't know how avoid the default CANVAS behavior that duplicates many times the line when I resize the windows. How do I solve it? Thank you
The answer is:
const positionCanvas = () => {
lineV.clearRect(0, 0, c.width, c.height); //<-- new line added in the code
//... rest of the code ommited
I have an image on canvas or a set of images that i draw on the canvas. I can draw them fine but I want to change the window level of the image on canvas by dragging the mouse over it. I've seen a lot of external javascript api's but they're quite huge and i don't want to use them for this purpose.
here's my JSFIDDLE that is basically drawing an image on the canvas
this is how my code looks
// Grab the Canvas and Drawing Context
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
// Create an image element
var img = document.createElement('IMG');
// When the image is loaded, draw it
img.onload = function () {
ctx.drawImage(img, 0, 0);
}
// Specify the src to load the image
img.src = "http://imgsv.imaging.nikon.com/lineup/lens/zoom/normalzoom/af-s_dx_18-140mmf_35-56g_ed_vr/img/sample/sample5_l.jpg";
i need something with basic JS or JQuery. it would be great if you can point me in the right direction using a sample code.
Thanks in advance.
If you want to use JavaScript, try something like:
<script>
var c = document.getElementById("drawingboard");
var ctx = c.getContext("2d");
var mouseDown = 0;
document.body.ontouchstart = function() {
mouseDown+=1;
}
document.body.ontouchend = function() {
mouseDown-=1;
}
function draw(event){
ctx.color='gold';
ctx.fillStyle = "lime";
ctx.drawImage(img,event.clientX,event.clientY);}
</script>
This is what I gained after some research:
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const rangeMin = level - window / 2;
const rangeMax = level + window / 2;
const totalSize = canvas.width * canvas.height;
for (let idx = 0; idx < totalSize; idx++) {
if (imageData.data[idx] < rangeMin) {
imageData.data[idx] = 0;
} else if (imageData.data[idx] > rangeMax) {
imageData.data[idx] = 255;
}
}
ctx.putImageData(imageData, 0, 0);