Socket
Socket
Sign inDemoInstall

scrolly-video

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

scrolly-video - npm Package Compare versions

Comparing version 0.0.17 to 0.0.18

8

.eslintrc.json

@@ -9,4 +9,4 @@ {

},
"extends": ["airbnb-base"],
"plugins": ["svelte3"],
"extends": ["airbnb-base", "plugin:prettier/recommended"],
"plugins": ["svelte3", "prettier"],
"overrides": [

@@ -26,4 +26,6 @@ {

"import/first": 0,
"camelcase": 0
"camelcase": 0,
"no-console": 0,
"prettier/prettier": 1
}
}

@@ -63,16 +63,32 @@ export default ScrollyVideo;

/**
* Transitions the video or the canvas to the proper frame
* Transitions the video or the canvas to the proper frame.
*
* @param jump
* @param options - Configuration options for adjusting the video playback.
* - jump: boolean - If true, the video currentTime will jump directly to the specified percentage. If false, the change will be animated over time.
* - transitionSpeed: number - Defines the speed of the transition when `jump` is false. Represents the duration of the transition in milliseconds. Default is 8.
* - easing: (progress: number) => number - A function that defines the easing curve for the transition. It takes the progress ratio (a number between 0 and 1) as an argument and returns the eased value, affecting the playback speed during the transition.
*/
transitionToTargetTime(jump: any): void;
transitioning: boolean;
transitionToTargetTime({ jump, transitionSpeed, easing, }: {
jump: any;
transitionSpeed?: number;
easing?: any;
}): void;
transitioningRaf: number;
/**
* Sets the currentTime as a percentage of the video duration.
* Sets the currentTime of the video as a specified percentage of its total duration.
*
* @param setPercentage
* @param jump
* @param setPercentage - The percentage of the video duration to set as the current time.
* @param options - Configuration options for adjusting the video playback.
* - jump: boolean - If true, the video currentTime will jump directly to the specified percentage. If false, the change will be animated over time.
* - transitionSpeed: number - Defines the speed of the transition when `jump` is false. Represents the duration of the transition in milliseconds. Default is 8.
* - easing: (progress: number) => number - A function that defines the easing curve for the transition. It takes the progress ratio (a number between 0 and 1) as an argument and returns the eased value, affecting the playback speed during the transition.
*/
setTargetTimePercent(setPercentage: any, jump: any): void;
setTargetTimePercent(setPercentage: any, options?: {}): void;
/**
* Simulate trackScroll programmatically (scrolls on page by percentage of video)
*
* @param percentage
*/
setScrollPercent(percentage: any): void;
/**
* Call to destroy this ScrollyVideo object

@@ -79,0 +95,0 @@ */

@@ -46,3 +46,4 @@ import UAParser from 'ua-parser-js';

// eslint-disable-next-line no-undef
if (scrollyVideoContainer instanceof Element) this.container = scrollyVideoContainer;
if (scrollyVideoContainer instanceof Element)
this.container = scrollyVideoContainer;
// otherwise it should better be an element

@@ -52,3 +53,4 @@ else if (typeof scrollyVideoContainer === 'string') {

this.container = document.getElementById(scrollyVideoContainer);
if (!this.container) throw new Error('scrollyVideoContainer must be a valid DOM object');
if (!this.container)
throw new Error('scrollyVideoContainer must be a valid DOM object');
} else {

@@ -103,3 +105,3 @@ throw new Error('scrollyVideoContainer must be a valid DOM object');

// Detect webkit (safari), because webkit requires special attention
const browserEngine = (new UAParser()).getEngine();
const browserEngine = new UAParser().getEngine();
// eslint-disable-next-line no-undef

@@ -120,8 +122,10 @@ this.isSafari = browserEngine.name === 'WebKit';

// Used for internally setting the scroll percentage based on built-in listeners
const containerBoundingClientRect = this.container.parentNode.getBoundingClientRect();
const containerBoundingClientRect =
this.container.parentNode.getBoundingClientRect();
// Calculate the current scroll percent of the video
const scrollPercent = (-containerBoundingClientRect.top)
const scrollPercent =
-containerBoundingClientRect.top /
// eslint-disable-next-line no-undef
/ (containerBoundingClientRect.height - window.innerHeight);
(containerBoundingClientRect.height - window.innerHeight);

@@ -131,3 +135,3 @@ if (this.debug) console.info('ScrollyVideo scrolled to', scrollPercent);

// Set the target time percent
this.setTargetTimePercent(scrollPercent, jump);
this.setTargetTimePercent(scrollPercent, { jump });
};

@@ -149,3 +153,3 @@

'loadedmetadata',
() => this.setTargetTimePercent(0, true),
() => this.setTargetTimePercent(0, { jump: true }),
{ once: true },

@@ -188,5 +192,4 @@ );

// Gets the width and height of the container
const {
width: containerWidth, height: containerHeight,
} = this.container.getBoundingClientRect();
const { width: containerWidth, height: containerHeight } =
this.container.getBoundingClientRect();

@@ -197,3 +200,7 @@ // Gets the width and height of the video frames

if (this.debug) console.info('Container dimensions:', [containerWidth, containerHeight]);
if (this.debug)
console.info('Container dimensions:', [
containerWidth,
containerHeight,
]);
if (this.debug) console.info('Element dimensions:', [width, height]);

@@ -218,5 +225,12 @@

if (this.useWebCodecs && this.src) {
videoDecoder(this.src, (frame) => { this.frames.push(frame); }, this.debug)
videoDecoder(
this.src,
(frame) => {
this.frames.push(frame);
},
this.debug,
)
.catch(() => {
if (this.debug) console.error('Error encountered while decoding video');
if (this.debug)
console.error('Error encountered while decoding video');
// Remove all decoded frames if a failure happens during decoding

@@ -231,3 +245,4 @@ this.frames = [];

if (this.frames.length === 0) {
if (this.debug) console.error('No frames were received from webCodecs');
if (this.debug)
console.error('No frames were received from webCodecs');
return;

@@ -238,3 +253,4 @@ }

this.frameRate = this.frames.length / this.video.duration;
if (this.debug) console.info('Received', this.frames.length, 'frames');
if (this.debug)
console.info('Received', this.frames.length, 'frames');

@@ -283,3 +299,9 @@ // Remove the video and add the canvas

// Draw the frame to the canvas context
this.context.drawImage(currFrame, 0, 0, currFrame.width, currFrame.height);
this.context.drawImage(
currFrame,
0,
0,
currFrame.width,
currFrame.height,
);
}

@@ -290,65 +312,140 @@ }

/**
* Transitions the video or the canvas to the proper frame
* Transitions the video or the canvas to the proper frame.
*
* @param jump
* @param options - Configuration options for adjusting the video playback.
* - jump: boolean - If true, the video currentTime will jump directly to the specified percentage. If false, the change will be animated over time.
* - transitionSpeed: number - Defines the speed of the transition when `jump` is false. Represents the duration of the transition in milliseconds. Default is 8.
* - easing: (progress: number) => number - A function that defines the easing curve for the transition. It takes the progress ratio (a number between 0 and 1) as an argument and returns the eased value, affecting the playback speed during the transition.
*/
transitionToTargetTime(jump) {
transitionToTargetTime({
jump,
transitionSpeed = this.transitionSpeed,
easing = null,
}) {
if (this.debug) {
console.info('Transitioning targetTime:', this.targetTime, 'currentTime:', this.currentTime);
console.info(
'Transitioning targetTime:',
this.targetTime,
'currentTime:',
this.currentTime,
);
}
// If we are already close enough to our target, pause the video and return.
// This is the base case of the recursive function
if (
// eslint-disable-next-line no-restricted-globals
isNaN(this.targetTime)
// If the currentTime is already close enough to the targetTime
|| Math.abs(this.currentTime - this.targetTime) < this.frameThreshold
) {
this.video.pause();
this.transitioning = false;
return;
}
const diff = this.targetTime - this.currentTime;
const distance = Math.abs(diff);
const duration = distance * 1000;
const isForwardTransition = diff > 0;
// Make sure we don't go out of time bounds
if (this.targetTime > this.video.duration) this.targetTime = this.video.duration;
if (this.targetTime < 0) this.targetTime = 0;
const tick = ({ startCurrentTime, startTimestamp, timestamp }) => {
const progress = (timestamp - startTimestamp) / duration;
// How far forward we need to transition
const transitionForward = this.targetTime - this.currentTime;
// if frameThreshold is too low to catch condition Math.abs(this.targetTime - this.currentTime) < this.frameThreshold
const hasPassedThreshold = isForwardTransition
? this.currentTime >= this.targetTime
: this.currentTime <= this.targetTime;
if (this.canvas) {
// Update currentTime and paint the closest frame
this.currentTime += transitionForward / (256 / this.transitionSpeed);
// If jump, we go directly to the frame
if (jump) this.currentTime = this.targetTime;
this.paintCanvasFrame(Math.floor(this.currentTime * this.frameRate));
} else if (jump || this.isSafari || this.targetTime - this.currentTime < 0) {
// We can't use a negative playbackRate, so if the video needs to go backwards,
// We have to use the inefficient method of modifying currentTime rapidly to
// get an effect.
this.video.pause();
this.currentTime += transitionForward / (64 / this.transitionSpeed);
// If jump, we go directly to the frame
if (jump) this.currentTime = this.targetTime;
this.video.currentTime = this.currentTime;
} else {
// Otherwise, we play the video and adjust the playbackRate to get a smoother
// animation effect.
const playbackRate = Math.max(Math.min((transitionForward) * 4, this.transitionSpeed, 16), 1);
if (this.debug) console.info('ScrollyVideo playbackRate:', playbackRate);
// eslint-disable-next-line no-restricted-globals
if (!isNaN(playbackRate)) {
this.video.playbackRate = playbackRate;
this.video.play();
// If we are already close enough to our target, pause the video and return.
// This is the base case of the recursive function
if (
// eslint-disable-next-line no-restricted-globals
isNaN(this.targetTime) ||
// If the currentTime is already close enough to the targetTime
Math.abs(this.targetTime - this.currentTime) < this.frameThreshold ||
hasPassedThreshold
) {
this.video.pause();
if (this.transitioningRaf) {
// eslint-disable-next-line no-undef
cancelAnimationFrame(this.transitioningRaf);
this.transitioningRaf = null;
}
return;
}
// Set the currentTime to the video's currentTime
this.currentTime = this.video.currentTime;
}
// Recursively calls ourselves until the animation is done.
// eslint-disable-next-line no-undef
// Make sure we don't go out of time bounds
if (this.targetTime > this.video.duration)
this.targetTime = this.video.duration;
if (this.targetTime < 0) this.targetTime = 0;
// How far forward we need to transition
const transitionForward = this.targetTime - this.currentTime;
const easedProgress =
easing && Number.isFinite(progress) ? easing(progress) : null;
const easedCurrentTime = isForwardTransition
? startCurrentTime +
easedProgress * Math.abs(distance) * transitionSpeed
: startCurrentTime -
easedProgress * Math.abs(distance) * transitionSpeed;
if (this.canvas) {
if (jump) {
// If jump, we go directly to the frame
this.currentTime = this.targetTime;
} else if (easedProgress) {
this.currentTime = easedCurrentTime;
} else {
this.currentTime += transitionForward / (256 / transitionSpeed);
}
this.paintCanvasFrame(Math.floor(this.currentTime * this.frameRate));
} else if (jump || this.isSafari || !isForwardTransition) {
// We can't use a negative playbackRate, so if the video needs to go backwards,
// We have to use the inefficient method of modifying currentTime rapidly to
// get an effect.
this.video.pause();
if (easedProgress) {
this.currentTime = easedCurrentTime;
} else {
this.currentTime += transitionForward / (64 / transitionSpeed);
}
// If jump, we go directly to the frame
if (jump) {
this.currentTime = this.targetTime;
}
this.video.currentTime = this.currentTime;
} else {
// Otherwise, we play the video and adjust the playbackRate to get a smoother
// animation effect.
const playbackRate = Math.max(
Math.min(transitionForward * 4, transitionSpeed, 16),
1,
);
if (this.debug)
console.info('ScrollyVideo playbackRate:', playbackRate);
// eslint-disable-next-line no-restricted-globals
if (!isNaN(playbackRate)) {
this.video.playbackRate = playbackRate;
this.video.play();
}
// Set the currentTime to the video's currentTime
this.currentTime = this.video.currentTime;
}
// Recursively calls ourselves until the animation is done.
if (typeof requestAnimationFrame === 'function') {
// eslint-disable-next-line no-undef
this.transitioningRaf = requestAnimationFrame((currentTimestamp) =>
tick({
startCurrentTime,
startTimestamp,
timestamp: currentTimestamp,
}),
);
}
};
if (typeof requestAnimationFrame === 'function') {
// eslint-disable-next-line no-undef
requestAnimationFrame(() => this.transitionToTargetTime());
this.transitioningRaf = requestAnimationFrame((startTimestamp) => {
tick({
startCurrentTime: this.currentTime,
startTimestamp,
timestamp: startTimestamp,
});
});
}

@@ -358,28 +455,57 @@ }

/**
* Sets the currentTime as a percentage of the video duration.
* Sets the currentTime of the video as a specified percentage of its total duration.
*
* @param setPercentage
* @param jump
* @param setPercentage - The percentage of the video duration to set as the current time.
* @param options - Configuration options for adjusting the video playback.
* - jump: boolean - If true, the video currentTime will jump directly to the specified percentage. If false, the change will be animated over time.
* - transitionSpeed: number - Defines the speed of the transition when `jump` is false. Represents the duration of the transition in milliseconds. Default is 8.
* - easing: (progress: number) => number - A function that defines the easing curve for the transition. It takes the progress ratio (a number between 0 and 1) as an argument and returns the eased value, affecting the playback speed during the transition.
*/
setTargetTimePercent(setPercentage, jump) {
setTargetTimePercent(setPercentage, options = {}) {
// eslint-disable-next-line
// The time we want to transition to
this.targetTime = Math.max(Math.min(setPercentage, 1), 0)
* ((this.frames.length && this.frameRate) ? this.frames.length
/ this.frameRate : this.video.duration);
this.targetTime =
Math.max(Math.min(setPercentage, 1), 0) *
(this.frames.length && this.frameRate
? this.frames.length / this.frameRate
: this.video.duration);
// If we are close enough, return early
if (!jump && Math.abs(this.currentTime - this.targetTime) < this.frameThreshold) return;
if (
!options.jump &&
Math.abs(this.currentTime - this.targetTime) < this.frameThreshold
)
return;
// If we are already transitioning, bail early
if (!jump && this.transitioning) return;
// Play the video if we are in video mode
if (!this.canvas && !this.video.paused) this.video.play();
// Set transitioning state to true and begin transition
this.transitioning = true;
this.transitionToTargetTime(jump);
this.transitionToTargetTime(options);
}
/**
* Simulate trackScroll programmatically (scrolls on page by percentage of video)
*
* @param percentage
*/
setScrollPercent(percentage) {
if (!this.trackScroll) {
console.warn('`setScrollPercent` requires enabled `trackScroll`');
return;
}
const parent = this.container.parentNode;
const { top, height } = parent.getBoundingClientRect();
// eslint-disable-next-line no-undef
const startPoint = top + window.pageYOffset;
// eslint-disable-next-line no-undef
const containerHeightInViewport = height - window.innerHeight;
const targetPoint = startPoint + containerHeightInViewport * percentage;
// eslint-disable-next-line no-undef
window.scrollTo({ top: targetPoint });
}
/**
* Call to destroy this ScrollyVideo object

@@ -390,4 +516,5 @@ */

// eslint-disable-next-line no-undef
if (this.trackScroll) window.removeEventListener('scroll', this.updateScrollPercentage);
if (this.trackScroll)
// eslint-disable-next-line no-undef
window.removeEventListener('scroll', this.updateScrollPercentage);

@@ -394,0 +521,0 @@ // eslint-disable-next-line no-undef

@@ -15,3 +15,4 @@ /* eslint-disable no-undef */

getData() {
if (this.idx !== this.size) throw new Error('Mismatch between size reserved and sized used');
if (this.idx !== this.size)
throw new Error('Mismatch between size reserved and sized used');
return this.data.slice(0, this.idx);

@@ -97,4 +98,8 @@ }

*/
const decodeVideo = (src, emitFrame, { VideoDecoder, EncodedVideoChunk, debug }) => new Promise(
(resolve, reject) => {
const decodeVideo = (
src,
emitFrame,
{ VideoDecoder, EncodedVideoChunk, debug },
) =>
new Promise((resolve, reject) => {
if (debug) console.info('Decoding video from', src);

@@ -112,17 +117,16 @@

output: (frame) => {
createImageBitmap(frame, { resizeQuality: 'low' })
.then((bitmap) => {
emitFrame(bitmap);
frame.close();
createImageBitmap(frame, { resizeQuality: 'low' }).then((bitmap) => {
emitFrame(bitmap);
frame.close();
if (decoder.decodeQueueSize <= 0) {
// Give it an extra half second to finish everything
setTimeout(() => {
if (decoder.state !== 'closed') {
decoder.close();
resolve();
}
}, 500);
}
});
if (decoder.decodeQueueSize <= 0) {
// Give it an extra half second to finish everything
setTimeout(() => {
if (decoder.state !== 'closed') {
decoder.close();
resolve();
}
}, 500);
}
});
},

@@ -138,7 +142,8 @@ error: (e) => {

if (info && info.videoTracks && info.videoTracks[0]) {
([{ codec }] = info.videoTracks);
[{ codec }] = info.videoTracks;
if (debug) console.info('Video with codec:', codec);
// Gets the avccbox used for reading extradata
const avccBox = mp4boxfile.moov.traks[0].mdia.minf.stbl.stsd.entries[0].avcC;
const avccBox =
mp4boxfile.moov.traks[0].mdia.minf.stbl.stsd.entries[0].avcC;
const extradata = getExtradata(avccBox);

@@ -172,28 +177,26 @@

// Fetches the file into arraybuffers
fetch(src)
.then((res) => {
const reader = res.body.getReader();
let offset = 0;
fetch(src).then((res) => {
const reader = res.body.getReader();
let offset = 0;
function appendBuffers({ done, value }) {
if (done) {
mp4boxfile.flush();
return null;
}
function appendBuffers({ done, value }) {
if (done) {
mp4boxfile.flush();
return null;
}
const buf = value.buffer;
buf.fileStart = offset;
offset += buf.byteLength;
mp4boxfile.appendBuffer(buf);
const buf = value.buffer;
buf.fileStart = offset;
offset += buf.byteLength;
mp4boxfile.appendBuffer(buf);
return reader.read().then(appendBuffers);
}
return reader.read().then(appendBuffers);
}
return reader.read().then(appendBuffers);
});
return reader.read().then(appendBuffers);
});
} catch (e) {
reject(e);
}
},
);
});

@@ -211,5 +214,13 @@ /**

// If our browser supports WebCodecs natively
if (typeof VideoDecoder === 'function' && typeof EncodedVideoChunk === 'function') {
if (debug) console.info('WebCodecs is natively supported, using native version...');
return decodeVideo(src, emitFrame, { VideoDecoder, EncodedVideoChunk, debug });
if (
typeof VideoDecoder === 'function' &&
typeof EncodedVideoChunk === 'function'
) {
if (debug)
console.info('WebCodecs is natively supported, using native version...');
return decodeVideo(src, emitFrame, {
VideoDecoder,
EncodedVideoChunk,
debug,
});
}

@@ -216,0 +227,0 @@

{
"name": "scrolly-video",
"version": "0.0.17",
"version": "0.0.18",
"description": "A component for scroll-based (or other externally controlled) playback.",

@@ -50,4 +50,7 @@ "main": "dist/scrolly-video.js",

"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-svelte3": "^3.4.0",
"prettier": "^3.2.4",
"rimraf": "^3.0.2",

@@ -66,3 +69,11 @@ "rollup": "^2.46.0",

"typescript": "^4.9.5"
},
"prettier": {
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false
}
}

@@ -92,4 +92,8 @@ # ScrollyVideo.js

***setCurrentTimePercent*** (`setPercentage | Number`): A number between 0 and 1 that specifies the percentage position of the video.
***setTargetTimePercent*** (`percentage: number`, `options: Options`): Pass a progress in between of 0 and 1 that specifies the percentage position of the video. Optionally, to customise experience of separate `setTargetTimePercent` calls you can utilize options:
- `transitionSpeed`: `number`
- `easing`: `(progress: number) => number`
Example: `setTargetTimePercent(0.5, { transitionSpeed: 12, easing: d3.easeLinear })`
## Technical Details and Cross Browser Differences

@@ -96,0 +100,0 @@ To make this library perform optimally in all browsers, three different approaches are taken to animating the video.

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc