New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@thegetty/quire-11ty

Package Overview
Dependencies
Maintainers
0
Versions
45
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thegetty/quire-11ty - npm Package Compare versions

Comparing version 1.0.0-rc.19 to 1.0.0-rc.20

239

_includes/web-components/image-sequence/index.js

@@ -6,5 +6,10 @@ import { LitElement, css, html, render, unsafeCSS } from 'lit'

// TODO: Ensure buffer size is > then performRotation increment (set bufferSize larger, make prefetch return a promise that we .then() )
// TODO: Raise and set an error message on the status-overlay component if fetches error out and cursor: not-allowed
/**
* @class ImageSequence
* @description A reactive Lit element for showing and interacting with a sequence of images.
*
* @todo on fetch error raise an error message on the status-overlay component
* and set cursor: not-allowed
*/
class ImageSequence extends LitElement {

@@ -15,6 +20,10 @@

static properties = {
bufferSize: {
animationFrame: {
type: Number,
state: true,
},
bufferSize: {
type: Number,
state: true,
},
didInteract: {

@@ -59,34 +68,41 @@ type: Boolean,

/**
* @property bufferReady
* @private
* @property #bufferWindow
*
* Returns an array of indexes to buffer
*
**/
get indexesToBuffer() {
* @returns an array of indexes included in `bufferSize` given `index`
*/
get #bufferWindow() {
// NB: Calculated one length higher to protect against numerical under run
return Array(this.bufferSize).fill(0).map( (_,i) => ( this.images.length + this.index + i - Math.round(this.bufferSize/2) ) % this.images.length )
const imageCount = this.images.length
const windowStart = imageCount + this.index - Math.round(this.bufferSize/2)
return Array(this.bufferSize)
.fill(0)
.map((_, i) => ((windowStart + i) % imageCount))
}
/**
* @property bufferReady
* @private
* @property #bufferReady
*
* Returns true if the buffer is loaded ahead and behind of `index`
*
**/
get bufferReady() {
return this.images.filter( (img,j) => this.indexesToBuffer.includes(j) && img !== null ).length === this.bufferSize
* @returns true if the buffer is loaded ahead and behind of `index`
*/
get #bufferReady() {
return this.images.filter((img, j) => this.#bufferWindow.includes(j) && img !== null ).length === this.bufferSize
}
/**
* @property buffered
*
* Returns true if the buffer is loaded ahead and behind of `index`
*
**/
* @property bufferedPct
* @returns percent of the buffer that is not-null
*/
get bufferedPct() {
return Math.floor( this.images.filter( (img,j) => this.indexesToBuffer.includes(j) && img !== null ).length / this.bufferSize * 100 )
return Math.floor( this.images.filter((img, j) => this.#bufferWindow.includes(j) && img !== null).length / this.bufferSize * 100 )
}
/**
* @property someImagesLoaded
* @returns true if there is at least one image loaded
*/
get someImagesLoaded() {
return this.images.some( i => i !== null )
return this.images.some((image) => image !== null)
}

@@ -99,17 +115,17 @@

*
* Fetches `url`, converts it into a blob and stores the image data, optionally drawing to the canvas.
* Fetches `url`, converts it into a blob and stores the image data in `seqIndex`.
*
* The in-flight fetch is stored in requests[seqIndex] for cancellation and request deduplication.
**/
#fetchImage(url,seqIndex) {
const req = new Request(url)
* The in-flight fetch is stored in this.requests for cancellation and request deduplication, nulled on completion.
*
* @returns {Promise} fetch resposne
*/
#fetchImage(url, seqIndex) {
if (this.requests[seqIndex]) return
if (this.requests[seqIndex]) {
return
}
const request = new Request(url)
this.requests[seqIndex] = fetch(req)
.then( (resp) => resp.blob() )
.then( (blob) => window.createImageBitmap(blob) )
.then( (bmp) => {
const response = fetch(request)
.then((response) => response.blob())
.then((blob) => window.createImageBitmap(blob))
.then((bmp) => {
if (this.intrinsicHeight === 0) {

@@ -120,22 +136,28 @@ this.intrinsicHeight = bmp.height

this.images[seqIndex] = bmp
// Draw if the user hasn't already gone past this index
if (this.index===seqIndex) {
this.#paintCanvas(bmp)
return
}
})
.then( () => {
.then(() => {
this.requests[seqIndex] = null
this.requestUpdate()
})
.catch( (err) => {
console.error(err)
.catch((error) => {
console.error(error)
})
this.requests[seqIndex] = response
return response
}
/**
* @function connectedCallback
*
* `lit` lifecycle method fired the first time the element is connected to the document
*
* Used to register our visibility IntersectionObserver
*/
connectedCallback() {
super.connectedCallback()
const callback = (entries,observer) => {
entries.forEach( (entry) => {
const callback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {

@@ -171,3 +193,3 @@ this.visible = true

const pctToBuffer = 0.2
this.bufferSize = Math.ceil( this.imageUrls.length * pctToBuffer )
this.bufferSize = Math.ceil(this.imageUrls.length * pctToBuffer)
this.blitting = null // null | animationFrameRequestId

@@ -191,2 +213,4 @@ this.images = Array(this.imageUrls.length).fill(null) // Array< null | ImageBitmap >

/**
* @function debounce
*
* Returns a function, that, as long as it continues to be invoked, will not

@@ -209,5 +233,6 @@ * be triggered. The function will be called after it stops being called for

* @param {boolean} immediate - whether to call the function immediately or at the end of the timeout
*
* @returns
*/
debounce(func, wait = 250, immediate = false) {
debounce(fn, wait = 250, immediate = false) {
let timeout = null

@@ -221,3 +246,3 @@

if (!immediate) {
func.apply(context, args)
fn.apply(context, args)
}

@@ -229,3 +254,3 @@ }

if (callNow) {
func.apply(context, args)
fn.apply(context, args)
}

@@ -235,2 +260,10 @@ }

/**
* @function handleMouseMove
*
* @param {Event.buttons}
* @param {Event.clientX}
*
* Sets interaction flag, hides overlays, and handles reversability check
*/
handleMouseMove({ buttons, clientX }) {

@@ -240,3 +273,2 @@ if (buttons) {

this.hideOverlays()
if (this.oldX) {

@@ -261,8 +293,2 @@ const deltaX = clientX - this.oldX

hideOverlays() {
this.querySelectorAll('.overlay').forEach((element) => {
element.classList.remove('visible')
})
}
/**

@@ -285,3 +311,3 @@ * @function nextImage

* Performs drawing operations against `this.context`
**/
*/
#draw(image) {

@@ -296,3 +322,3 @@ this.context.drawImage(image,0,0)

* Paints the `canvas` element with the image from this.index
**/
*/
#paintCanvas(image) {

@@ -326,19 +352,28 @@ if (!this.canvasRef.value) {

* `lit` lifecycle method for changed properties
*
**/
*/
willUpdate(changedProperties) {
// Determine the animation indices, preload them, and then do the rotation
if (changedProperties.has('rotateToIndex') && this.rotateToIndex!==false) {
this.performRotation(this.rotateToIndex)
const frameCount = this.rotateToIndex - this.index
const animationIndices = Array(frameCount).fill(0).map((_, i) => this.index + i + 1)
this.#preloadImages(animationIndices).then(this.animateRotation(this.rotateToIndex))
}
// Draws `animationFrame` directly to canvas (for use )
if (changedProperties.has('animationFrame')) {
if (!this.animationFrame) { return }
if (this.images[this.animationFrame] === null) { return }
this.#paintCanvas(this.images[this.animationFrame])
}
if (changedProperties.has('index') && this.someImagesLoaded) {
this.#preloadImages()
if (this.bufferReady) {
this.#paintCanvas()
}
this.#preloadImages().then(() => {
this.animateRotation(this.index)
})
}
// Load enough to prepare for interaction
if (changedProperties.has('visible') && !this.visible) {
this.#preloadImages()
this.#preloadImages().then(() => this.#paintCanvas())
}

@@ -350,18 +385,20 @@ }

*
* Loads the k images behind and ahead of this.index
**/
#preloadImages() {
if (!this.images.some(i => i === null)) { return }
* Loads images of this.indexesToBuffer -- ahead and behind `this.index`
*
* @returns {Promise} - Promise resolution for all preloads
*/
#preloadImages(bufferWindow) {
if (!this.images.some(i => i === null)) return Promise.all([]);
// Really just making buffer counting ergonomic / readable here
// const indexesToBuf = Array(this.bufferSize).fill(0).map( (_,i) => ( this.images.length + this.index + i - Math.round(this.bufferSize/2) ) % this.images.length )
this.images.forEach( (image,i) => {
// Skip anything out of our range or already loaded
if ( !this.indexesToBuffer.includes(i) || image !== null ) {
return
}
const indexesToLoad = bufferWindow ?? this.#bufferWindow
const imageRequests = this.images
.map((image, i) => {
// Skip anything out of our range or already loaded
if (!indexesToLoad.includes(i) || image !== null) return null;
const url = this.imageUrls[i]
return this.#fetchImage(url, i);
})
.filter((imageRequest) => imageRequest)
const url = this.imageUrls[i]
this.#fetchImage(url,i)
})
return Promise.all(imageRequests)
}

@@ -372,4 +409,7 @@

*/
performRotation(indexToMove) {
if (this.index === indexToMove) return
animateRotation(untilIndex) {
if (this.animationFrame === untilIndex) return
this.animationFrame = this.index
const interval = setInterval(() => {

@@ -379,4 +419,6 @@ /**

*/
if (this.index === indexToMove) {
if (this.animationFrame === untilIndex) {
this.index = untilIndex
this.rotateToIndex = false
this.synchronizeSequenceInstances()
}

@@ -386,3 +428,3 @@ /**

*/
if (this.rotateToIndex !== indexToMove) {
if (this.rotateToIndex !== untilIndex) {
clearInterval(interval)

@@ -394,3 +436,3 @@ return

*/
this.nextImage()
this.animationFrame += 1
}, this.transition)

@@ -410,3 +452,2 @@ }

// TODO: Consult quire team for expected behavior here
synchronizeSequenceInstances() {

@@ -425,3 +466,2 @@ clearTimeout(this.updateTimer)

render() {
const descriptionOverlay = this.isInteractive ?

@@ -441,17 +481,16 @@ html`<div slot='overlay' class="overlay ${ this.didInteract === false ? 'visible' : '' }"><span class="description">

return html`<div class="image-sequence ${ this.bufferReady ? '' : 'loading' } ${ this.isInteractive ? 'interactive' : '' }">
<slot name="loading-overlay">
<div slot="loading-overlay" class='${ this.bufferReady ? '' : 'visible'} loading overlay'>
<div class="buffering-indicator">Loading Image Sequence&nbsp;(${ this.bufferedPct }%)...</div>
</div>
</slot>
<canvas ${ref(this.canvasRef)} height="${this.intrinsicHeight}" width="${this.intrinsicWidth}" class="${ this.someImagesLoaded ? 'visible' : '' } ${ this.didInteract ? '' : 'fade-in' }" slot="images"></canvas>
<slot name="overlay">
${ descriptionOverlay }
</slot>
</div>`
return html`<div class="image-sequence ${this.#bufferReady ? '' : 'loading'} ${this.isInteractive ? 'interactive' : ''}">
<slot name="loading-overlay">
<div slot="loading-overlay" class='${ this.#bufferReady ? '' : 'visible'} loading overlay'>
<div class="buffering-indicator">Loading Image Sequence&nbsp;(${ this.bufferedPct }%)...</div>
</div>
</slot>
<canvas ${ref(this.canvasRef)} height="${this.intrinsicHeight}" width="${this.intrinsicWidth}" class="${this.someImagesLoaded ? 'visible' : ''} ${this.didInteract ? '' : 'fade-in'}" slot="images"></canvas>
<slot name="overlay">
${descriptionOverlay}
</slot>
</div>`
}
}
customElements.define('q-image-sequence',ImageSequence)
customElements.define('q-image-sequence', ImageSequence)
# q-image-sequence
Provides a simple Lit webcomponent for handling image sequences
Provides a Lit webcomponent for displaying image sequences with a click-and-drag behvaior
## Overview
The component loads a comma-separated list of images passed to the component into a view that moves forward / backward in the sequence of images by dragging in the UI or modifying the `rotate-to-index` or `index` properties.
The component loads a comma-separated list of images passed to the component and allows the user to move forward / backward in the sequence of images by dragging in the UI or modifying the `rotate-to-index` or `index` properties (eg, via a `ref` tag or in the runtime application js).
### Parameters
### Attributes
The component takes a few parameters:
The component takes a few attributes:
- `index` - Index to show in the sequence
- `interactive` - Whether to allow forward/backward user interaction
- `reverse` - Whether to allow reversing in the sequence (ie, left-swipe from index=0)
- `rotate-to-index` - Index to rotate to
- `images` - Comma-separated list of images. *Note that images must use proper URI escaping to pass comma-separated parsing!*
- `images` - Comma-separated list of images. *Note that images must use proper URI escaping to pass comma-separated-value parsing and HTML element attribute escaping!*
### Architecture
The component markup has three parts:
- A loading overlay that is shown when the sequence is buffering
- A call to action overlay shown after load but before interaction that invites click-drag/swipe gestures (called "descriptionOverlay" in the code)
- A canvas element managed via a Lit `ref`
Styles / css are loaded into the Lit template from `styles.js`.
If it is a non-interactive component it loads the first image of the sequence only (though remains responsive to `rotate-to-index` attribute changes).
If the component is interactive, it buffers a percentage of the total images passed at startup and waits for interaction (showing the loading-overlay and overlay as required).
On interaction, the component changes its `index` property, triggering a `drawImage` command to paint the canvas, cancelling the last animation frame drawn and storing the animation frame ID, filling its buffer as necessary. When in-flight requests are made they are deduplicated by storing the Promise returned by `fetch()`, releasing it after the image is buffered.

@@ -29,3 +29,3 @@ import { css } from 'lit'

align-items: center;
position: absolute;
position: relative;
top: 0;

@@ -44,2 +44,3 @@ left: 0;

opacity: 1;
pointer-events: auto;
}

@@ -46,0 +47,0 @@

@@ -6,3 +6,3 @@ const filters = require('./filters')

* Add Collections and Apply Transforms
*
*
* Nota bene: The Eleventy API does not make collections data accessible

@@ -23,3 +23,3 @@ * from the plugin context. Adding `collections` and `transforms` sequentially

eleventyConfig.addCollection('allSorted', function (collectionApi) {
return collectionApi.getAll().sort(sortCollection)
return collectionApi.getAllSorted().sort(sortCollection)
})

@@ -43,3 +43,3 @@

collections[name] = collectionApi
.getAll()
.getAllSorted()
.filter(filters[name])

@@ -46,0 +46,0 @@ .sort(sortCollection)

@@ -15,5 +15,22 @@ # Changelog

## [1.0.0-rc.20]
### Changed
- Refactor image-sequence preloadImages to accept an indices parameter of indices to preload and return the promise of all fetch responses
- Adds an animationIndex property for managing animation when rotating
### Fixed
- image-sequence rotation to an index on load
- display of the rotation call to action
## [1.0.0-rc.19]
### Changed
- Performance improvments and refactoring for image sequences:
- Refactor `q-image-sequence` component to load a buffer of image bitmaps from the image URLs passed to it
- Refactor `q-image-sequence` to use encapsulated styles at the module level
## [1.0.0-rc.18]

@@ -23,3 +40,3 @@

- Performance improvments for images:
- Performance improvements for images:
- Refactor `figure` subcomponent composition using named `slot` elements for data, ui, slides, and styles

@@ -26,0 +43,0 @@ - Refactor `lightbox` components to generate slides dynamically from JSON data

{
"name": "@thegetty/quire-11ty",
"version": "1.0.0-rc.19",
"version": "1.0.0-rc.20",
"description": "Quire 11ty static site generator",

@@ -5,0 +5,0 @@ "keywords": [

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