# Image Scroller Widget

> Single tall image that scrolls on hover (or on scroll) inside a fixed-height viewport — long screenshots, scrolling timelines, vertical comic strips. Smallest non-trivial Pro widget at 218 lines.

**Class file:** `includes/Elements/Image_Scroller.php` (218 lines)
**Slug:** `image-scroller` (widget id `eael-image-scroller`)
**Public docs:** <https://essential-addons.com/elementor/docs/image-scroller/>
**Pro-shared:** Pro-only.

## Overview

Display a tall image (e.g. webpage screenshot, infographic) inside a smaller viewport. On hover (or scroll trigger), the image vertically scrolls to reveal full content. Pure CSS animation, no vendor libs.

## Pro vs Lite

Pro-only.

## File Map

| File | Role |
| --- | --- |
| `includes/Elements/Image_Scroller.php` | Widget class (218 lines — smallest non-trivial widget) |
| `src/css/view/image-scroller.scss` → `assets/front-end/css/view/image-scroller.min.css` | Styling + scroll animation keyframes |
| `src/js/view/image-scroller.js` → `assets/front-end/js/view/image-scroller.min.js` | (Possibly only event binding; CSS handles animation) |
| `config.php` entry `'image-scroller'` | Self CSS + self JS |

## Architecture

- **Tiny widget** — 218 lines. Single setting set, no per-preset bloat.
- **Composes Lite's `Helper`** (line 9).
- **CSS-driven scroll** — animation runs via CSS `:hover` + `transform: translateY()` over `transition-duration`. JS may bind scroll-into-viewport trigger.
- **No template directory**.

## Render Output

```html
<div class="eael-image-scroller-wrap"
     style="height:{viewport-height}px"
     data-trigger="hover|scroll">
  <div class="eael-image-scroller-inner">
    <img src="{image}" alt="{alt}" />
  </div>
</div>
```

On hover (CSS) or scroll-into-view (JS), `.eael-image-scroller-inner` `transform: translateY(-{image_height - viewport_height})` animates the image up.

## Controls Reference

| Control id | Tab → Section | Type | Purpose |
| --- | --- | --- | --- |
| Image | Content → Image | MEDIA | Tall image |
| Viewport height | Content → Settings | SLIDER | Visible box height |
| Trigger | Content → Settings | CHOOSE | `hover` / `scroll-into-view` |
| Animation duration | Content → Settings | SLIDER | Transition speed |

## Conditional Dependencies

```text
trigger = 'hover' → standard hover behaviour
trigger = 'scroll-into-view' → IntersectionObserver branch in JS
```

## JavaScript Lifecycle

Minimal. If trigger = scroll-into-view, JS uses IntersectionObserver to add a class that runs the animation when viewport enters.

## Hooks & Filters

Standard widget render.

## Common Issues

| Symptom | Cause | Fix |
| --- | --- | --- |
| Image doesn't scroll on touch | No touch-equivalent of hover | Use scroll-into-view trigger for mobile |
| Animation too fast / slow | Default duration mismatch | Adjust animation-duration control |
| Image distorted | Width 100% with auto height stretching | Verify image source dimensions |

## Known Limitations

- **Two trigger modes only** — no manual scrollbar option
- **No `prefers-reduced-motion`** — animation always runs
- **Image must be naturally tall** (height > viewport) — short images result in no animation
- **Smallest documented Pro widget** — most other widgets are 1,000+ lines

## Cross-References

- Sibling: [`img-comparison.md`](img-comparison.md), [`image-hotspots.md`](image-hotspots.md)
- Shared patterns: [`_patterns.md`](_patterns.md)
