2023-07-24 17:00:07 +00:00
|
|
|
import { html } from 'lit';
|
|
|
|
import { property, query, state } from 'lit/decorators.js';
|
|
|
|
import { watch } from '../../internal/watch.js';
|
2024-02-09 15:12:47 +00:00
|
|
|
import componentStyles from '../../styles/component.styles.js';
|
2023-07-24 17:00:07 +00:00
|
|
|
import ShoelaceElement from '../../internal/shoelace-element.js';
|
|
|
|
import SlIcon from '../icon/icon.component.js';
|
|
|
|
import styles from './animated-image.styles.js';
|
|
|
|
import type { CSSResultGroup } from 'lit';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @summary A component for displaying animated GIFs and WEBPs that play and pause on interaction.
|
|
|
|
* @documentation https://shoelace.style/components/animated-image
|
|
|
|
* @status stable
|
|
|
|
* @since 2.0
|
|
|
|
*
|
|
|
|
* @dependency sl-icon
|
|
|
|
*
|
|
|
|
* @event sl-load - Emitted when the image loads successfully.
|
|
|
|
* @event sl-error - Emitted when the image fails to load.
|
|
|
|
*
|
|
|
|
* @slot play-icon - Optional play icon to use instead of the default. Works best with `<sl-icon>`.
|
|
|
|
* @slot pause-icon - Optional pause icon to use instead of the default. Works best with `<sl-icon>`.
|
|
|
|
*
|
2024-02-08 17:46:31 +00:00
|
|
|
* @part control-box - The container that surrounds the pause/play icons and provides their background.
|
2023-07-24 17:00:07 +00:00
|
|
|
*
|
|
|
|
* @cssproperty --control-box-size - The size of the icon box.
|
|
|
|
* @cssproperty --icon-size - The size of the play/pause icons.
|
|
|
|
*/
|
|
|
|
export default class SlAnimatedImage extends ShoelaceElement {
|
2024-02-09 15:12:47 +00:00
|
|
|
static styles: CSSResultGroup = [componentStyles, styles];
|
2023-07-24 17:00:07 +00:00
|
|
|
static dependencies = { 'sl-icon': SlIcon };
|
|
|
|
|
|
|
|
@query('.animated-image__animated') animatedImage: HTMLImageElement;
|
|
|
|
|
|
|
|
@state() frozenFrame: string;
|
|
|
|
@state() isLoaded = false;
|
|
|
|
|
|
|
|
/** The path to the image to load. */
|
|
|
|
@property() src: string;
|
|
|
|
|
|
|
|
/** A description of the image used by assistive devices. */
|
|
|
|
@property() alt: string;
|
|
|
|
|
|
|
|
/** Plays the animation. When this attribute is remove, the animation will pause. */
|
|
|
|
@property({ type: Boolean, reflect: true }) play: boolean;
|
|
|
|
|
|
|
|
private handleClick() {
|
|
|
|
this.play = !this.play;
|
|
|
|
}
|
|
|
|
|
|
|
|
private handleLoad() {
|
|
|
|
const canvas = document.createElement('canvas');
|
|
|
|
const { width, height } = this.animatedImage;
|
|
|
|
canvas.width = width;
|
|
|
|
canvas.height = height;
|
|
|
|
canvas.getContext('2d')!.drawImage(this.animatedImage, 0, 0, width, height);
|
|
|
|
this.frozenFrame = canvas.toDataURL('image/gif');
|
|
|
|
|
|
|
|
if (!this.isLoaded) {
|
|
|
|
this.emit('sl-load');
|
|
|
|
this.isLoaded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private handleError() {
|
|
|
|
this.emit('sl-error');
|
|
|
|
}
|
|
|
|
|
|
|
|
@watch('play', { waitUntilFirstUpdate: true })
|
|
|
|
handlePlayChange() {
|
|
|
|
// When the animation starts playing, reset the src so it plays from the beginning. Since the src is cached, this
|
|
|
|
// won't trigger another request.
|
|
|
|
if (this.play) {
|
|
|
|
this.animatedImage.src = '';
|
|
|
|
this.animatedImage.src = this.src;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@watch('src')
|
|
|
|
handleSrcChange() {
|
|
|
|
this.isLoaded = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return html`
|
|
|
|
<div class="animated-image">
|
|
|
|
<img
|
|
|
|
class="animated-image__animated"
|
|
|
|
src=${this.src}
|
|
|
|
alt=${this.alt}
|
|
|
|
crossorigin="anonymous"
|
|
|
|
aria-hidden=${this.play ? 'false' : 'true'}
|
|
|
|
@click=${this.handleClick}
|
|
|
|
@load=${this.handleLoad}
|
|
|
|
@error=${this.handleError}
|
|
|
|
/>
|
|
|
|
|
|
|
|
${this.isLoaded
|
|
|
|
? html`
|
|
|
|
<img
|
|
|
|
class="animated-image__frozen"
|
|
|
|
src=${this.frozenFrame}
|
|
|
|
alt=${this.alt}
|
|
|
|
aria-hidden=${this.play ? 'true' : 'false'}
|
|
|
|
@click=${this.handleClick}
|
|
|
|
/>
|
|
|
|
|
|
|
|
<div part="control-box" class="animated-image__control-box">
|
|
|
|
<slot name="play-icon"><sl-icon name="play-fill" library="system"></sl-icon></slot>
|
|
|
|
<slot name="pause-icon"><sl-icon name="pause-fill" library="system"></sl-icon></slot>
|
|
|
|
</div>
|
|
|
|
`
|
|
|
|
: ''}
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
}
|