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.18 to 1.0.0-rc.19

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

3

_includes/components/head.js

@@ -61,4 +61,3 @@ const path = require('path')

<script src="https://cdn.jsdelivr.net/npm/@digirati/canvas-panel-web-components@1.0.56" type="module"></script>
<script src="https://cdn.jsdelivr.net/npm/@iiif/vault-helpers@latest/dist/index.umd.js"></script>
<script src="/_assets/javascript/application/canvas-panel-web-components-1.0.68.js" type="module"></script>

@@ -65,0 +64,0 @@ ${publisherLinks}

import { LitElement, css, html, render, unsafeCSS } from 'lit'
import { createRef, ref } from 'lit/directives/ref.js'
class ImageSequence extends LitElement {
import { imageSequenceStyles } from './styles.js'
static styles = css`
:host {
position: relative;
display: flex;
justify-content: center;
max-width: 100vw;
max-height: 100vh;
}
// 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
.image-sequence.interactive {
height: 100%;
cursor: grab;
}
class ImageSequence extends LitElement {
.overlay {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0);
color: white;
transition: all 0.25s linear;
}
static styles = [ imageSequenceStyles ]
.overlay.visible {
opacity: 1;
}
.overlay:not(.visible) {
opacity: 0;
}
.overlay:hover {
background: rgba(0,0,0,0.6);
}
.overlay.loading.visible {
animation: loading-overlay 1s infinite alternate;
}
@keyframes loading-overlay {
from {
opacity: 0.6;
}
to {
opacity: 1;
}
}
.description {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
opacity: 0;
transition: opacity 0.25s linear;
}
.description__icon {
fill: white;
height: 2em;
}
.overlay:hover .description {
opacity: 1;
}
slot[name='images'] img {
display: block;
pointer-events: none;
user-select: none;
width: 100%;
height: 100%;
}
slot[name='images'] img:not(.placeholder) {
position: absolute;
top: 0;
left: 0;
}
slot[name='images'] img.visible {
opacity: 1;
object-fit: contain;
}
slot[name='images'] img:not(.visible) {
opacity: 0;
}
slot[name='images'] img.fade-in {
animation: fade-in 0.25s 1 linear;
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
slot[name='placeholder-image'] img.loading {
opacity: 1;
filter: brightness(0.4);
animation: loading-image 1s linear infinite alternate;
}
@keyframes loading-image {
from {
filter: brightness(0.4);
}
to {
filter: brightness(0.2);
}
}
slot[name='placeholder-image'] img {
opacity: 0;
transition: opacity 0.25s linear;
object-fit: cover;
}`
static properties = {
index: {
bufferSize: {
type: Number,
state: true,
},

@@ -141,6 +22,2 @@ didInteract: {

},
visible: {
type: Boolean,
state: true,
},
images: {

@@ -150,2 +27,17 @@ type: Array,

},
imageUrls: {
attribute: 'image-urls',
type: String,
},
index: {
type: Number,
},
intrinsicHeight: {
type: Number,
state: true,
},
intrinsicWidth: {
type: Number,
state: true,
},
rotateToIndex: {

@@ -157,16 +49,106 @@ attribute: 'rotate-to-index',

type: Number,
},
visible: {
type: Boolean,
state: true,
}
}
get allImagesLoaded() {
return this.imagesLoaded === this.images.length
canvasRef = createRef()
/**
* @property bufferReady
*
* Returns an array of indexes to buffer
*
**/
get indexesToBuffer() {
// 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 )
}
// Fires when this component is visible
_loadImages() {
const imageElements = this.images.map( (img,i) => {
fetch(img).then( this.imagesLoaded++ )
})
/**
* @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
}
/**
* @property buffered
*
* Returns true if the buffer is loaded ahead and behind of `index`
*
**/
get bufferedPct() {
return Math.floor( this.images.filter( (img,j) => this.indexesToBuffer.includes(j) && img !== null ).length / this.bufferSize * 100 )
}
get someImagesLoaded() {
return this.images.some( i => i !== null )
}
/**
* @function #fetchImage
* @param url {string} - image URL to fetch
* @param seqIndex {Number} - index to store this image
*
* Fetches `url`, converts it into a blob and stores the image data, optionally drawing to the canvas.
*
* The in-flight fetch is stored in requests[seqIndex] for cancellation and request deduplication.
**/
#fetchImage(url,seqIndex) {
const req = new Request(url)
if (this.requests[seqIndex]) {
return
}
this.requests[seqIndex] = fetch(req)
.then( (resp) => resp.blob() )
.then( (blob) => window.createImageBitmap(blob) )
.then( (bmp) => {
if (this.intrinsicHeight === 0) {
this.intrinsicHeight = bmp.height
this.intrinsicWidth = bmp.width
}
this.images[seqIndex] = bmp
// Draw if the user hasn't already gone past this index
if (this.index===seqIndex) {
this.#paintCanvas(bmp)
return
}
})
.then( () => {
this.requests[seqIndex] = null
this.requestUpdate()
})
.catch( (err) => {
console.error(err)
})
}
connectedCallback() {
super.connectedCallback()
const callback = (entries,observer) => {
entries.forEach( (entry) => {
if (entry.isIntersecting) {
this.visible = true
observer.disconnect()
}
})
}
const options = { root: null, threshold: 0.5 }
// Observes this component against the viewport to trigger image preloads
const io = new IntersectionObserver(callback, options)
io.observe(this)
}
constructor() {

@@ -177,3 +159,4 @@ super()

this.description = 'Click and drag horizontally to rotate image'
this.images = this.getAttribute('items').split(',')
this.imageUrls = this.getAttribute('items').split(',')
this.posterImageSrc = this.imageUrls.length > 0 ? this.imageUrls[0] : ''
this.isContinuous = this.getAttribute('continuous') === 'true'

@@ -186,26 +169,16 @@ this.isInteractive = this.getAttribute('interactive') === 'true'

// Internal state
this.imageData = []
const pctToBuffer = 0.2
this.bufferSize = Math.ceil( this.imageUrls.length * pctToBuffer )
this.blitting = null // null | animationFrameRequestId
this.images = Array(this.imageUrls.length).fill(null) // Array< null | ImageBitmap >
this.requests = Array(this.imageUrls.length).fill(null) // Array< null | Promise >
this.visible = false
this.index = 0
this.intrinsicHeight = 0
this.intrinsicWidth = 0
this.oldIndex = null
this.oldX = null
this.imagesLoaded = 0
this.totalCanvases = this.images.length
this.imageCount = this.imageUrls.length
// Set up observable and mouse events
this._loadImages()
// TODO: Setup observer and run isVisible() when we're at least one screen away
// const io = new IntersectionObserver( (entries,observer) => {
// entries.forEach(entry => {
// if (entry.intersectionRatio > 0) {
// this.visible = true
// this._isObservable()
// }
// })
// })
// io.observe(this)
if (this.isInteractive) {

@@ -257,12 +230,5 @@ this.addEventListener('mousemove', this.handleMouseMove.bind(this))

// getClampedIndex(index) {
// if (!this.images) return 0
// return Math.max(Math.min(index, this.images.length - 1), 0)
// }
handleMouseMove({ buttons, clientX }) {
if (buttons) {
if (this.didInteract) {
this.didInteract = true
}
this.didInteract = true

@@ -275,8 +241,8 @@ this.hideOverlays()

this.isReversed
? this.previousCanvas(deltaIndex)
: this.nextCanvas(deltaIndex)
? this.previousImage(deltaIndex)
: this.nextImage(deltaIndex)
} else if (deltaX < 0) {
this.isReversed
? this.nextCanvas(deltaIndex)
: this.previousCanvas(deltaIndex)
? this.nextImage(deltaIndex)
: this.previousImage(deltaIndex)
}

@@ -290,9 +256,2 @@ }

hideAllImages() {
this.images.forEach((element) => {
element.classList.remove('visible')
})
}
hideOverlays() {

@@ -304,22 +263,60 @@ this.querySelectorAll('.overlay').forEach((element) => {

hideImage(imageElement) {
imageElement.className = ''
/**
* @function nextImage
* @param {Integer} n Number of steps between start index and end index
*
* Set the sequence image to the index `n` steps after the current index
*/
nextImage(n=1) {
const newIndex = this.index + n >= this.imageCount
? this.index + n - this.imageCount
: this.index + n
this.index = newIndex
}
hideLoadingOverlay() {
this.querySelector('.overlay').classList.remove('visible')
/**
* @function #draw
*
* Performs drawing operations against `this.context`
**/
#draw(image) {
this.context.drawImage(image,0,0)
}
/**
* Set the sequence canvas to the index `n` steps after the current index
* @function #paintCanvas
* @param {ImageBitmap} - image - image to paint
*
* @param {Integer} n Number of steps between start index and end index
*/
nextCanvas(n=1) {
const newIndex = this.index + n >= this.totalCanvases
? this.index + n - this.totalCanvases
: this.index + n
this.index = newIndex
* Paints the `canvas` element with the image from this.index
**/
#paintCanvas(image) {
if (!this.canvasRef.value) {
return
}
this.context ??= this.canvasRef.value.getContext('2d')
if (image) {
window.cancelAnimationFrame(this.blitting)
this.blitting = window.requestAnimationFrame( () => this.#draw(image) )
return
}
if (!this.images[this.index]) {
this.#fetchImage(this.imageUrls[this.index],this.index)
return
}
window.cancelAnimationFrame(this.blitting)
this.blitting = window.requestAnimationFrame( () => this.#draw(this.images[this.index]) )
}
/**
* @function willUpdate
* @param changedProperties
*
* `lit` lifecycle method for changed properties
*
**/
willUpdate(changedProperties) {

@@ -329,6 +326,39 @@ if (changedProperties.has('rotateToIndex') && this.rotateToIndex!==false) {

}
if (changedProperties.has('index') && this.someImagesLoaded) {
this.#preloadImages()
if (this.bufferReady) {
this.#paintCanvas()
}
}
if (changedProperties.has('visible') && !this.visible) {
this.#preloadImages()
}
}
/**
* Animates a rotation by stepping through canvases from the current index to the provided `newValue`
* @function #preloadImages
*
* Loads the k images behind and ahead of this.index
**/
#preloadImages() {
if (!this.images.some(i => i === null)) { return }
// 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 url = this.imageUrls[i]
this.#fetchImage(url,i)
})
}
/**
* Animates a rotation by stepping through images from the current index to the provided `newValue`
*/

@@ -341,4 +371,2 @@ performRotation(indexToMove) {

*/
// TODO:
console.log(this.index)
if (this.index === indexToMove) {

@@ -355,5 +383,5 @@ this.rotateToIndex = false

/**
* Step through canvases
* Step through images
*/
this.nextCanvas()
this.nextImage()
}, this.transition)

@@ -363,8 +391,8 @@ }

/**
* Set the sequence canvas to the index `n` indices before the current index
* Set the sequence image to the index `n` indices before the current index
* @param {Integer} n Number of steps between start index and end index
*/
previousCanvas(n=1) {
previousImage(n=1) {
const newIndex = this.index - n < 0
? this.totalCanvases + this.index - n
? this.imageCount + this.index - n
: this.index - n

@@ -374,10 +402,3 @@ this.index = newIndex

// TODO: Add this to the class list if no interaction
showImage(imageElement, shouldFadeIn) {
if (!imageElement) return
shouldFadeIn && imageElement.classList.add('fade-in')
imageElement.classList.add('visible')
}
// TODO: maybe do this in an await this.updateComplete callback? -- synchro to the on-page resources?
// TODO: Consult quire team for expected behavior here
synchronizeSequenceInstances() {

@@ -397,6 +418,4 @@ clearTimeout(this.updateTimer)

const loadingOverlayElement = html`<div slot='loading-overlay' class='${ this.allImagesLoaded ? '' : 'visible'} loading overlay'>Loading Image Sequence...</div>`
const descriptionOverlay = this.isInteractive ?
html`<div slot='overlay' class="overlay ${ this.oldX === null ? 'visible' : '' }"><span class="description">
html`<div slot='overlay' class="overlay ${ this.didInteract === false ? 'visible' : '' }"><span class="description">
<svg class="description__icon">

@@ -414,12 +433,9 @@ <symbol id="rotation-icon" viewBox="0 0 24 24">

return html`<div class="image-sequence ${ this.allImagesLoaded ? '' : 'loading' } ${ this.isInteractive ? 'interactive' : '' }">
<slot name="placeholder-image">
<img slot="placeholder-image" class="${ this.allImagesLoaded ? '' : 'loading' } placeholder" src="${ this.images.length > 0 ? this.images[0] : '' }" >
</slot>
return html`<div class="image-sequence ${ this.bufferReady ? '' : 'loading' } ${ this.isInteractive ? 'interactive' : '' }">
<slot name="loading-overlay">
${ loadingOverlayElement }
</slot>
<slot name="images">
<img slot="images" class="${ this.allImagesLoaded ? 'visible' : '' } ${ this.didInteract ? '' : 'fade-in' } " src="${this.images[this.index]}">
<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">

@@ -426,0 +442,0 @@ ${ descriptionOverlay }

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

## [1.0.0-rc.19]
## [1.0.0-rc.18]

@@ -17,0 +20,0 @@

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

@@ -63,2 +63,3 @@ "keywords": [

"@11ty/is-land": "^3.0.1",
"@digirati/canvas-panel-web-components": "^1.0.68",
"@iiif/parser": "^1.1.2",

@@ -65,0 +66,0 @@ "@iiif/vault": "^0.9.22",

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