Components/ImageGeneration

ImageGeneration

A WebGL pixel-mosaic placeholder for generated or slow-loading images.

VerifiedSince 0.6.2

Usage

Wraps one element, paints a shader inside it, and dissolves into the image once it is ready. Requires the three peer dependency (>=0.149.0).

Presets

preset picks the effect; images is the pool to reveal from.

Loading demo...

Loading Flag with a Known Image

When the card stays mounted after the job ends, reveal or hide the image through the ref. Trigger the reveal after the new images reach the component, and leave paused unset.

<script setup lang="ts">
import { ref, watch } from 'vue'

const props = defineProps<{ generating: boolean, src: string }>()
const cell = ref()

// flush: 'post' fires after the component has the new images.
watch(() => props.generating, (generating) => {
  // Generating: fade any shown image back to the shader.
  // Done: reveal the image and hold it until the next run.
  if (generating)
    cell.value?.triggerHide()
  else
    cell.value?.triggerReveal({ hold: 'manual' })
}, { flush: 'post' })
</script>

<template>
  <TxImageGeneration ref="cell" :images="[props.src]" role="img" :aria-label="props.generating ? 'Generating image' : 'Result'" :aria-busy="props.generating">
    <div style="width: 320px; height: 200px; border-radius: 12px" />
  </TxImageGeneration>
</template>

Regenerate

triggerRegenerate() works only while an image is showing: the image breaks into cells, churns, and the next image from the pool dissolves in.

<script setup lang="ts">
import { ref } from 'vue'

const cell = ref()
const variants = ['/a.jpg', '/b.jpg', '/c.jpg']
</script>

<template>
  <TxImageGeneration ref="cell" preset="pixels-mechanic" :images="variants">
    <div style="width: 320px; height: 320px; border-radius: 20px" />
  </TxImageGeneration>
  <TxButton @click="cell?.triggerReveal({ hold: 'manual' })">Show</TxButton>
  <TxButton @click="cell?.triggerRegenerate({ durationMs: 3000 })">Regenerate</TxButton>
</template>

Best Practices

  • Give the wrapped child an explicit width and height; the wrapper is inline-block and sizes itself to the child.
  • Pass images before triggering a reveal; with an empty pool every trigger is a silent no-op.
  • As a placeholder that never reveals an image, animate only while working (:paused="!isGenerating"). When it does reveal one, leave paused unset: a paused card silently ignores reveals.
  • Serve reveal images same-origin or with CORS headers; cross-origin images still reveal, but triggerRegenerate() can't sample their colors and falls back to the preset palette.
  • Use sweep-gradient with a lower strength for quiet grid tiles; keep the pixel mosaics for the main result.

API Reference

Props

PropTypeDefaultDescription
preset'pixels-organic' | 'pixels-mechanic' | 'sweep-gradient''pixels-organic'Bundled effect: soft mosaic, grid-locked mosaic, or diagonal sweep.
theme'auto' | 'dark' | 'light''auto'Theme; auto follows the page or OS theme live.
strengthnumber10–1 scales canvas opacity; above 1 it boosts the palette instead.
speednumber1Scales the whole effect clock (drift, churn, flicker).
pixelScalenumber1Pixel-cell size multiplier; the reveal dissolve stays in step.
cardBgstringpreset defaultCard background, also used for the shader's contrast logic.
colors(string | null | undefined)[]preset palettePalette override, one entry per shader slot.
imagesstring | string[][]Reveal pool; picks at random, never repeating the previous image.
autoRevealbooleanfalseLoops shader → reveal → hold → hide on its own.
revealDelayRange[number, number][2, 4]Random delay between reveals, in seconds.
revealInitialDelaynumber | [number, number]jitterOne-time delay before the first reveal.
revealHoldMsnumber | [number, number]2000How long the image stays fully visible.
revealFadeOutMsnumber300Fade back to the shader.
borderRadiusnumberauto-detectedCard corner radius in CSS px.
pausedbooleanfalseFreezes the shader and auto-reveal; reveal, hide, and regenerate do nothing while paused.
fragmentShaderstringbundledReplacement GLSL 1.00 fragment shader; applies page-wide while set.
excludeSrcs() => string[] | Set<string> | nullnoneSources this pick must avoid, for instances sharing a pool.

Events

EventPayloadDescription
cycleImageGenerationCycleEventAuto-reveal phase changes (idle → reveal → visible → hide).

Slots

SlotPropsDescription
default-The card element the effect sizes itself to.

Exposed Methods

MethodDescription
elementThe root wrapper <div> (HTMLDivElement); null while unmounted.
triggerReveal({ hold?: 'auto' | 'manual' })Runs one reveal, held until triggerHide() with manual; no-op mid-pass or with empty images.
triggerHide()Fades the image back to the shader; no-op when nothing is showing.
triggerRegenerate({ durationMs?, tintFromImage?, autoReveal? })Breaks the shown image into churning cells, then dissolves in the next; no-op unless an image is showing.
isImageActive()true while an image is revealing, visible, or hiding.

Overview

  • One shared THREE.WebGLRenderer and WebGL context serve the whole page; each card copies its frame into its own 2D canvas.
  • Frame rate is capped at 10 fps, the GL canvas at a device pixel ratio of 1.25, and the visible canvas at 2.
  • Cards pause offscreen (IntersectionObserver, 64 px margin), the loop stops when no card is active, and WebGL context loss is handled.
  • Decoded images are cached by URL across cards and drawn like object-fit: cover, center-cropped.
  • The corner radius comes from the child's computed border-top-left-radius and applies to all four corners.
  • prefers-reduced-motion is not handled; wire it yourself when needed, such as :paused="reduced && generating".

Technologies

  • engine/** and presets/** are ported from Jakubantalik/Libraries · img-fx (MIT © Jakub Antalik).
  • Source: packages/tuffex/packages/components/src/image-generation/.
查看源码
packages/tuffex/packages/components/src/image-generation/index.ts