Skip to content

Commit

Permalink
feat: support multiple sprites and individual thumbnails
Browse files Browse the repository at this point in the history
cf #57.
  • Loading branch information
phloxic committed Dec 3, 2023
1 parent e22b512 commit 20770f3
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 23 deletions.
34 changes: 20 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

# videojs-sprite-thumbnails

Plugin to display thumbnails from a sprite image when hovering over the progress bar.
Plugin to display thumbnails when hovering over the progress bar.

## Compatibility

Expand All @@ -31,9 +31,9 @@ For Video.js v6.x, v7.x compatibility switch to the [vjs6-7-compat branch](https

## Features

- works with single or multiple sprites containing thumbnails and individual thumbnail images
- easy to [configure](#configuration)
- uses [existing mouse time tooltip](#constraints)
- focuses on use case of thumbnails combined in a sprite image only

## Installation

Expand All @@ -55,7 +55,7 @@ This is the simplest case. Get the script in whatever way you prefer and include
<script>
const player = videojs('my-video');
// set up 160x90 thumbnails in sprite.jpg, 1 per second
// set up 160x90 thumbnails in single sprite.jpg, 1 per second
player.spriteThumbnails({
url: 'https://example.com/sprite.jpg',
width: 160,
Expand All @@ -79,10 +79,13 @@ require('videojs-sprite-thumbnails');

const player = videojs('my-other-video');

// More than 0 rows in combination with inserting {index) in the url
// signals a sprite sequence.
player.spriteThumbnails({
interval: 3,
url: 'https://example.com/another-sprite.jpg',
columns: 10,
url: 'https://example.com/sprite_sequence-{index}.jpg',
columns: 5,
rows: 5,
width: 120,
height: 90
});
Expand Down Expand Up @@ -119,11 +122,12 @@ Or load the latest Video.js v8.x compatible release of the plugin via [script ta

option | type | mandatory | default | description
------ | ---- | --------- | ------- | -----------
`url` | String | &#10004; | | Location of sprite image.
`url` | String | &#10004; | | Location of image(s). Must be set by user. For multiple images the filename must contain the template `{index}` which is replaced by the zero based index number of the image in the sequence.
`width` | Integer | &#10004; | | Width of a thumbnail in pixels.
`height` | Integer | &#10004; | | Height of a thumbnail in pixels.
`columns` | Integer | &#10004; | | Number of columns in sprite image.
`interval` | Number | | `1` | Interval between thumbnail frames in seconds.
`columns` | Integer | &#10004; | | Number of thumbnail columns per image. Set both `columns` and `rows` to `1` for individual thumbnails.
`rows` | Integer | | `0` | Number of thumbnail rows per image. If set to greater than `0`, the plugin will expect a sequence of images. Set both `rows` and `columns` to `1` for individual thumbnails.
`interval` | Number | | `1` | Interval between thumbnails in seconds.
`responsive` | Integer | | `600` | Width of player in pixels below which thumbnails are responsive. Set to `0` to disable.
`downlink` | Number | | `1.5` | Minimum of required [NetworkInformation downlink][downlink] where supported. Set to `0` to disable.

Expand All @@ -133,7 +137,7 @@ The plugin is initialized at player setup. This is sufficient when the player wi

The plugin also monitors all video sources on [loadstart](https://docs.videojs.com/player#event:loadstart) for a `spriteThumbnails` property which configures the plugin for this specific video. A typical use case are [playlists](#playlist-example).

The sprite image is then loaded on demand, if and when the user moves the pointer on the seekbar.
The image(s) are then loaded on demand, when the cursor hovers or moves over the progress bar.

### Playlist example

Expand All @@ -145,7 +149,7 @@ const playlist = [

// only needed once, even if alternaive source is picked
spriteThumbnails: {
url: 'https://example.com/thumbnails1.jpg'
url: 'https://example.com/thumbnails1-{index}.jpg'
}
}, {
type: 'video/mp4',
Expand All @@ -154,7 +158,7 @@ const playlist = [
type: 'application/x-mpegurl',
src: 'https://example.com/video2.m3u8',
spriteThumbnails: {
url: 'https://example.com/thumbnails2.jpg'
url: 'https://example.com/thumbnails2-{index}.jpg'
}
}]
];
Expand All @@ -170,7 +174,8 @@ const player = videojs('myplayer', {
spriteThumbnails: {
width: 160,
height: 90,
columns: 5
columns: 5,
rows: 5
}
}
});
Expand All @@ -195,10 +200,11 @@ The call can also be chained directly to the [manual plugin setup](https://docs.
```js
const player = videojs('example-player');
player.spriteThumbnails({
url: 'https://example.com/thumbnails.jpg',
url: 'https://example.com/thumbnails-{index}.jpg',
width: 240,
height: 100
columns: 7
columns: 7,
rows: 6
}).log.level('debug');
```

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "videojs-sprite-thumbnails",
"version": "2.2.0-dev",
"description": "Plugin to display thumbnails from a sprite image when hovering over the progress bar.",
"description": "Plugin to display thumbnails when hovering over the progress bar.",
"repository": {
"type": "git",
"url": "[email protected]:phloxic/videojs-sprite-thumbnails.git"
Expand Down
10 changes: 8 additions & 2 deletions src/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ const Plugin = videojs.getPlugin('plugin');
* Default plugin options
*
* @param {String} url
* Location of sprite image(s). Must be set by user. Default: ''.
* Location of image(s). Must be set by user. For multiple images the
* filename must contain the template {index} which is replaced by the
* zero based index number of the image in the sequence. Default: ''.
* @param {Integer} width
* Width of a thumbnail in pixels. Must be set by user. Default: 0.
* @param {Integer} height
* Height of a thumbnail in pixels. Must be set by user. Default: 0.
* @param {Integer} columns
* Number of columns per sprite. Must be set by user.
* Number of thumbnail columns per image. Must be set by user.
* @param {Integer} rows
* Number of thumbnail rows per image. If set to greater than 0, the
* plugin will expect a sequence of images. Default: 0.
* @param {Number} interval
* Interval between thumbnail frames in seconds. Default: 1.
* @param {Integer} responsive
Expand All @@ -28,6 +33,7 @@ const defaults = {
width: 0,
height: 0,
columns: 0,
rows: 0,
interval: 1,
responsive: 600,
downlink: 1.5
Expand Down
27 changes: 21 additions & 6 deletions src/sprite-thumbnails.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,21 @@ const spriteThumbs = (player, plugin, options) => {
const columns = options.columns;
const responsive = options.responsive;

const rows = Math.ceil(duration / interval * columns);
const position = dom.getPointerPosition(seekBarEl, evt).x * duration / interval;
const rowDuration = interval * columns;
let rows = options.rows || Math.ceil(duration / rowDuration);
// spriteDuration is needed to calculate idx and rows of last sprite
const spriteDuration = rowDuration * rows;

let position = dom.getPointerPosition(seekBarEl, evt).x * duration;
// for single sprites idx is always 0
const idx = Math.floor(position / spriteDuration);

// if (idx == 0) position /= interval
position = (position - spriteDuration * idx) / interval;
// Last (or only) sprite may have less rows, calculate required rows for it
if (idx === Math.floor(duration / spriteDuration)) {
rows -= Math.floor((spriteDuration * (idx + 1) - duration) / rowDuration);
}

const scaleFactor = responsive && playerWidth < responsive ?
playerWidth / responsive : 1;
Expand All @@ -62,7 +75,7 @@ const spriteThumbs = (player, plugin, options) => {
const topOffset = -scaledHeight - Math.max(0, seekBarTop - controlsTop);

const tooltipStyle = {
backgroundImage: `url("${options.url}")`,
backgroundImage: `url("${options.url.replace('{index}', idx)}")`,
backgroundRepeat: 'no-repeat',
backgroundPosition: `${cleft}px ${ctop}px`,
backgroundSize: `${scaledWidth * columns}px ${scaledHeight * rows}px`,
Expand All @@ -83,9 +96,10 @@ const spriteThumbs = (player, plugin, options) => {

const intCheck = (opt) => {
const val = options[opt];
const min = opt !== 'rows' ? 1 : 0;

if (parseInt(val, 10) !== val || val < 1) {
log(`${opt} must be an integer greater than 0`);
if (parseInt(val, 10) !== val || val < min) {
log(`${opt} must be an integer greater than ${min - 1}`);
return false;
}
return true;
Expand All @@ -111,7 +125,8 @@ const spriteThumbs = (player, plugin, options) => {

plugin.setState({
ready: !!(mouseTimeTooltip && options.url &&
intCheck('width') && intCheck('height') && intCheck('columns') && dl),
intCheck('width') && intCheck('height') && intCheck('columns') &&
intCheck('rows') && dl),
diagnostics: true
});
};
Expand Down

0 comments on commit 20770f3

Please sign in to comment.