Closed kiwwwipl closed 3 years ago
I'm gonna go out on a hutch here... this sounds like a race condition.
It could be the video is not loaded yet, but you're asking for the duration, thus it's like NaN
because it doesn't know what to tell you.
I'd try first testing to see if all the video.duration
work listening for an event.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event
const video = document.querySelector('video');
video.addEventListener('canplay', (event) => {
console.log(video.duration);
});
If you can prove the durations are all valid, then you need to setup a listener to wait for the video to be ready, before doing anything with the video.duration
.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/duration
Wow, thank you for answering so quickly - appreciate it!
The problem is that the video.duration
doesn't work only when put inside a ScrollMagic scene as a parameter. Outside the scene it returns correct value even without listener.
What's your actual code, since your example says duration: ???
?
FYI, I didn't write ScrollMagic, but I'm happy to help.
Sure, appreciate your help.
Well, first I triedvideoFullScreen.duration
and then I just put some fixed value just to make the scene work in some part at least.
When you console.log(videoFullScreen.duration)
it doesn't show the duration after it's loaded? That's weird.
A work around is to add a data attribute to the video tag output from the cms. Like <video data-duration="25" />
or something like that. You could then parse the attribute.
By the way, I know you're trying to scrub the video timeline, and I'll just put out there that I don't recommend this. It comes out quite choppy trying to constantly set the video current time. I'd recommend looking into doing this with a canvas animation animation.
Ok, I'll try the data attribute workaround - thanks!
About the video timeline - you're right, I'm not really happy with the end result. Any chance you could give me a bit more specific tip/link to some resources?
You're trying to do the Apple video tricks? Last I saw they use a sequence of images. See this url https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/10-fall-into-case/0000.jpg
. Try changing the 0000.jpg
to other numbers, like 0010.jpg
. It's not a video they are scrubbing, it's an image sequence.
They then probably preload the images, or a certain number of them, and then on scroll change, update the image written to the canvas.
I actually made this as a feature of ScrollScene here but removed it as it was far too beta and buggy to be released. I dug up my old code I was working with. Maybe it'll help you.
const canvas = {
element: null,
extension: 'jpg',
frames: null,
height: 820,
width: 1458,
source: null,
...options,
}
canvas.element.width = canvas.width
canvas.element.height = canvas.height
const ctx = canvas.element.getContext('2d')
ctx.imageSmoothingEnabled = true
ctx.imageSmoothingQuality = 'high'
let prevProgress = 0
const getSeqNum = progress =>
padNumber(Math.min(canvas.frames, Math.round(lerp(prevProgress, progress, 0.1) * 100)), 4)
const images = new Array()
for (let index = 0; index < canvas.frames; index++) {
images.push(canvas.source + padNumber(index + 1, 4) + '.' + canvas.extension)
}
const preloadedImages = new Array()
const preload = imageArr => {
for (let index = 0; index < imageArr.length; index++) {
preloadedImages[index] = new Image()
preloadedImages[index].src = imageArr[index]
}
}
preload(images)
const updateCanvas = progress => {
/* Get the image */
const img = new Image()
img.src = `https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/02-head-bob-turn/${getSeqNum(
(progress * 100) / canvas.frames,
)}.jpg`
prevProgress = progress
/* Wait til the image is loaded */
img.onload = function() {
ctx.drawImage(img, 0, 0) /* Draw the image to the canvas */
}
}
Scene.on('start', function(event) {
if (event.state === 'BEFORE') {
/* Set to the first frame when entering scene */
updateCanvas('0001')
}
})
Scene.on('progress', function(event) {
if (event.state === 'DURING') {
updateCanvas(event.progress)
}
})
Scene.on('end', function(event) {
if (event.state === 'AFTER') {
/* Set to the last frame when leaving scene */
updateCanvas(padNumber(canvas.frames, 4))
}
})
}```
The data attribute workaround worked like a charm :)
About the canvas way: if I understand correctly first you have to export A LOT of individual images from a video/render, right?
That's right. It's an image sequence, not a video.
Happy the data attribute worked. 🤟
I have a loop of scenes with videos that play with the scroll (loop because it's integrated with WordPress ACF Flexible fields).
What I'm trying to do is to make the duration of the whole scene same as duration of the video of the scene (on the front-end there'll be a presentation with different videos). I tried simply
video.duration
, but it behaves in a weird way: sometimes it works, sometimes not (NaN). Is there a solution for this? Here is my code: