Last active
July 10, 2025 13:14
-
-
Save remino/ee50cba70365b3e48f2d21dab4dc75a6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
anim.gif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
set -eu | |
log() { | |
echo "[genanim] $@" | |
} | |
require() { | |
missing_bin=0 | |
for bin in "$@"; do | |
if ! which "$bin" > /dev/null 2>&1; then | |
missing_bin=1 | |
_error "Required: $bin" | |
fi | |
done | |
if [ $missing_bin -ne 0 ]; then | |
_fatal "$E_MISSING_APP" "One or more executables or apps are missing." | |
fi | |
} | |
which magick > /dev/null 2>&1 || (echo "ImageMagick 7 required." >&2 && exit 16) | |
log "Starting animated GIF generation…" | |
TMPDIR=$(mktemp -d) | |
cleanup() { | |
[ -n "${TMPDIR:-}" ] && [ -d "$TMPDIR" ] && rm -rf "$TMPDIR" | |
log "Cleaned up temporary files." | |
} | |
trap cleanup EXIT INT | |
svg() { | |
cat << SVG | |
<svg width="300" height="300" xmlns="http://www.w3.org/2000/svg"> | |
<defs> | |
<linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%"> | |
<stop offset="0%" stop-color="black"/> | |
<stop offset="33.3333%" stop-color="white"/> | |
<stop offset="66.6667%" stop-color="black"/> | |
<stop offset="100%" stop-color="white"/> | |
</linearGradient> | |
</defs> | |
<rect x="0" y="0" width="300" height="300" fill="url(#g)" /> | |
</svg> | |
SVG | |
} | |
SIZE=240 | |
FRAMES=48 | |
FPS=12 | |
DURATION=$(awk "BEGIN {print 100.0 / $FPS}") | |
OUTPUT=anim.gif | |
log "Temp folder: $TMPDIR" | |
log "Creating SVG file..." | |
svg > "$TMPDIR/grad.svg" | |
log "Creating diagonal gradient base…" | |
magick \ | |
-density 300 \ | |
"$TMPDIR/grad.svg" \ | |
-resize "$((SIZE * 3))x$((SIZE * 3))" \ | |
"$TMPDIR/gradient.png" | |
log "Applying ordered dithering (B/W)…" | |
magick "$TMPDIR/gradient.png" \ | |
-ordered-dither o8x8 \ | |
"$TMPDIR/dithered.png" | |
log "Generating $FRAMES frames…" | |
for i in $(seq 0 $((FRAMES - 1))); do | |
OFFSET=$(awk "BEGIN {print int($i * ($SIZE * 2) / $FRAMES)}") | |
magick "$TMPDIR/dithered.png" \ | |
-crop "${SIZE}x${SIZE}+$OFFSET+$OFFSET" +repage \ | |
"$TMPDIR/frame_$(printf "%02d" "$i").gif" | |
log "Frame $i → offset: $OFFSET" | |
done | |
log "Assembling animation…" | |
magick -delay "$DURATION" -loop 0 "$TMPDIR"/frame_*.gif \ | |
-filter point -resize 960x960\! anim.gif | |
if which image_optim > /dev/null 2>&1; then | |
log "Found image_optim." | |
log "Optimizing with image_optim…" | |
image_optim "$OUTPUT" | |
else | |
log "No image_optim found. Skipped optimization." | |
fi | |
log "Done! Output: $OUTPUT" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment