Plugin Directory

Changeset 3390988


Ignore:
Timestamp:
11/06/2025 10:19:27 AM (4 months ago)
Author:
serend
Message:

Update plugin to version 1.0.3

Location:
serend-animations
Files:
1 added
2 deleted
15 edited

Legend:

Unmodified
Added
Removed
  • serend-animations/trunk/CHANGELOG.md

    r3368477 r3390988  
    55The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
    66and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
     7
     8## [1.0.3] - 2025-01-29
     9
     10### Added
     11- **Direction buttons for ALL animations**: Visual arrow buttons to select animation direction (top, right, bottom, left)
     12- **Slow Motion toggle**: Make any animation 1.5x slower with a simple toggle switch
     13- **Universal direction support**: All animation types (Fade, Zoom, Rotate, Bounce, Flip) now support all 4 directions
     14- **20 new animation variants**: Each animation type now has 4 directional variants
     15  - Zoom: zoom-in, zoom-in-left, zoom-in-right, zoom-in-top
     16  - Rotate: rotate-in, rotate-in-left, rotate-in-right, rotate-in-top
     17  - Bounce: bounce-in, bounce-in-left, bounce-in-right, bounce-in-top
     18  - Flip: flip-in (X-axis), flip-in-left (Y-axis), flip-in-right (Y-axis), flip-in-top (X-axis)
     19- **Visual direction picker**: Intuitive icon-based buttons with rotated arrow SVGs
     20- **Slow modifier class**: New `animation-slow` class works across all animations
     21
     22### Changed
     23- **Simplified UI**: Cleaner dropdown with base animation types (Fade, Zoom, Rotate, Bounce, Flip)
     24- **Direction selection**: Moved from dropdown to visual button grid for better UX
     25- **Removed "Fade In Slow"**: Replaced with universal "Slow Motion" toggle for all animations
     26- **Delay behavior**: Animation delay no longer triggers editor preview (frontend-only)
     27- **Future-proof selectors**: Frontend JavaScript now uses attribute selectors for better maintainability
     28
     29### Fixed
     30- Fixed `scroll-fade-in-top` and other directional variants not triggering `fade-in-visible` class
     31- Frontend animation detection now works with all new animation variants
     32- Editor preview optimization (delay changes don't re-trigger preview)
     33
     34### Technical Improvements
     35- **CSS architecture**: Organized animations by type with clear section comments
     36- **Universal slow modifier**: Single `animation-slow` class affects all animation types
     37- **Optimized CSS selectors**: Wildcard selectors for performance (`[class*="scroll-"]`)
     38- **Reduced code duplication**: Eliminated need for separate "slow" animation variants
     39- **Better scalability**: New direction system makes adding animations easier
     40- **Smarter JavaScript**: Attribute-based selectors catch all current and future animation variants
     41- **Enhanced keyframes**: Complete set of directional keyframes for editor preview
     42- **Improved class management**: Better handling of animation-slow and direction classes
     43
     44## [1.0.2] - 2025-01-28
     45
     46### Added
     47- Live animation preview in Gutenberg editor with "Show Animation" button
     48- Automatic animation preview when selecting or changing animations
     49- Automatic animation preview when changing delay values
     50- Support for all third-party blocks (Yoast SEO, Rank Math, etc.)
     51- Animation preview works in iframe editor context
     52
     53### Fixed
     54- Animation classes now properly cleaned up when switching animations
     55- No more orphaned class fragments (like `-left`, `-right`, `-slow`)
     56- Correct animation detection for compound classes (e.g., `scroll-fade-in-left`)
     57- Elements remain visible in editor while supporting animation previews
     58- Animation dropdown now shows correct selection after changing animations
     59- Fade In animations now work correctly across all variants
     60
     61### Changed
     62- Improved className management with array-based filtering
     63- Animation class extraction now prioritizes longest matches first
     64- Simplified animation preview logic (always animates block element)
     65- Removed debug console logs for production-ready code
     66- Enhanced editor CSS with `:not()` selectors for better performance
     67
     68### Technical Improvements
     69- Uses `className` attribute directly (WordPress standard)
     70- Compatible with server-side rendered blocks
     71- Cleaner code structure with reduced complexity
     72- Better iframe handling for editor context
     73- Optimized CSS specificity for editor styles
    774
    875## [1.0.1] - 2025-01-27
  • serend-animations/trunk/includes/assets/css/animations.css

    r3368477 r3390988  
    11/* Gutenberg Animations CSS */
    22
    3 /* Scroll Fade-In Animation (Standard - von unten) */
    4 .scroll-fade-in {
     3/* ============================================
     4   FADE ANIMATIONS
     5   ============================================ */
     6
     7/* Fade-In from bottom (default) */
     8body:not(.block-editor-page) .scroll-fade-in {
    59    opacity: 0;
    610    transform: translateY(30px);
     
    812}
    913
    10 .scroll-fade-in.fade-in-visible {
     14body:not(.block-editor-page) .scroll-fade-in.fade-in-visible {
    1115    opacity: 1;
    1216    transform: translateY(0);
     
    1418
    1519/* Fade-In from left */
    16 .scroll-fade-in-left {
     20body:not(.block-editor-page) .scroll-fade-in-left {
    1721    opacity: 0;
    1822    transform: translateX(-30px);
     
    2024}
    2125
    22 .scroll-fade-in-left.fade-in-visible {
     26body:not(.block-editor-page) .scroll-fade-in-left.fade-in-visible {
    2327    opacity: 1;
    2428    transform: translateX(0);
     
    2630
    2731/* Fade-In from right */
    28 .scroll-fade-in-right {
     32body:not(.block-editor-page) .scroll-fade-in-right {
    2933    opacity: 0;
    3034    transform: translateX(30px);
     
    3236}
    3337
    34 .scroll-fade-in-right.fade-in-visible {
     38body:not(.block-editor-page) .scroll-fade-in-right.fade-in-visible {
    3539    opacity: 1;
    3640    transform: translateX(0);
    3741}
    3842
    39 /* Slower animation (from bottom) */
    40 .scroll-fade-in-slow {
    41     opacity: 0;
    42     transform: translateY(30px);
    43     transition: opacity 1.2s ease-out, transform 1.2s ease-out;
    44 }
    45 
    46 .scroll-fade-in-slow.fade-in-visible {
     43/* Fade-In from top */
     44body:not(.block-editor-page) .scroll-fade-in-top {
     45    opacity: 0;
     46    transform: translateY(-30px);
     47    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     48}
     49
     50body:not(.block-editor-page) .scroll-fade-in-top.fade-in-visible {
    4751    opacity: 1;
    4852    transform: translateY(0);
    4953}
    5054
    51 /* Additional animations */
    52 
    53 /* Zoom-In Effekt */
    54 .scroll-zoom-in {
    55     opacity: 0;
    56     transform: scale(0.8);
    57     transition: opacity 0.8s ease-out, transform 0.8s ease-out;
    58 }
    59 
    60 .scroll-zoom-in.fade-in-visible {
    61     opacity: 1;
    62     transform: scale(1);
    63 }
    64 
    65 /* Rotation Effekt */
    66 .scroll-rotate-in {
    67     opacity: 0;
    68     transform: rotate(10deg) scale(0.9);
    69     transition: opacity 0.8s ease-out, transform 0.8s ease-out;
    70 }
    71 
    72 .scroll-rotate-in.fade-in-visible {
    73     opacity: 1;
    74     transform: rotate(0deg) scale(1);
    75 }
    76 
    77 /* Bounce Effekt */
    78 .scroll-bounce-in {
     55/* ============================================
     56   ZOOM ANIMATIONS
     57   ============================================ */
     58
     59/* Zoom-In from bottom (default) */
     60body:not(.block-editor-page) .scroll-zoom-in {
     61    opacity: 0;
     62    transform: scale(0.8) translateY(30px);
     63    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     64}
     65
     66body:not(.block-editor-page) .scroll-zoom-in.fade-in-visible {
     67    opacity: 1;
     68    transform: scale(1) translateY(0);
     69}
     70
     71/* Zoom-In from left */
     72body:not(.block-editor-page) .scroll-zoom-in-left {
     73    opacity: 0;
     74    transform: scale(0.8) translateX(-30px);
     75    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     76}
     77
     78body:not(.block-editor-page) .scroll-zoom-in-left.fade-in-visible {
     79    opacity: 1;
     80    transform: scale(1) translateX(0);
     81}
     82
     83/* Zoom-In from right */
     84body:not(.block-editor-page) .scroll-zoom-in-right {
     85    opacity: 0;
     86    transform: scale(0.8) translateX(30px);
     87    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     88}
     89
     90body:not(.block-editor-page) .scroll-zoom-in-right.fade-in-visible {
     91    opacity: 1;
     92    transform: scale(1) translateX(0);
     93}
     94
     95/* Zoom-In from top */
     96body:not(.block-editor-page) .scroll-zoom-in-top {
     97    opacity: 0;
     98    transform: scale(0.8) translateY(-30px);
     99    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     100}
     101
     102body:not(.block-editor-page) .scroll-zoom-in-top.fade-in-visible {
     103    opacity: 1;
     104    transform: scale(1) translateY(0);
     105}
     106
     107/* ============================================
     108   ROTATE ANIMATIONS
     109   ============================================ */
     110
     111/* Rotate-In from bottom (default) */
     112body:not(.block-editor-page) .scroll-rotate-in {
     113    opacity: 0;
     114    transform: rotate(-10deg) scale(0.9) translateY(30px);
     115    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     116}
     117
     118body:not(.block-editor-page) .scroll-rotate-in.fade-in-visible {
     119    opacity: 1;
     120    transform: rotate(0deg) scale(1) translateY(0);
     121}
     122
     123/* Rotate-In from left */
     124body:not(.block-editor-page) .scroll-rotate-in-left {
     125    opacity: 0;
     126    transform: rotate(-10deg) scale(0.9) translateX(-30px);
     127    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     128}
     129
     130body:not(.block-editor-page) .scroll-rotate-in-left.fade-in-visible {
     131    opacity: 1;
     132    transform: rotate(0deg) scale(1) translateX(0);
     133}
     134
     135/* Rotate-In from right */
     136body:not(.block-editor-page) .scroll-rotate-in-right {
     137    opacity: 0;
     138    transform: rotate(10deg) scale(0.9) translateX(30px);
     139    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     140}
     141
     142body:not(.block-editor-page) .scroll-rotate-in-right.fade-in-visible {
     143    opacity: 1;
     144    transform: rotate(0deg) scale(1) translateX(0);
     145}
     146
     147/* Rotate-In from top */
     148body:not(.block-editor-page) .scroll-rotate-in-top {
     149    opacity: 0;
     150    transform: rotate(-10deg) scale(0.9) translateY(-30px);
     151    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     152}
     153
     154body:not(.block-editor-page) .scroll-rotate-in-top.fade-in-visible {
     155    opacity: 1;
     156    transform: rotate(0deg) scale(1) translateY(0);
     157}
     158
     159/* ============================================
     160   BOUNCE ANIMATIONS
     161   ============================================ */
     162
     163/* Bounce-In from bottom (default) */
     164body:not(.block-editor-page) .scroll-bounce-in {
    79165    opacity: 0;
    80166    transform: translateY(50px);
     
    82168}
    83169
    84 .scroll-bounce-in.fade-in-visible {
     170body:not(.block-editor-page) .scroll-bounce-in.fade-in-visible {
    85171    opacity: 1;
    86172    transform: translateY(0);
    87173}
    88174
    89 /* Flip Effekt */
    90 .scroll-flip-in {
     175/* Bounce-In from left */
     176body:not(.block-editor-page) .scroll-bounce-in-left {
     177    opacity: 0;
     178    transform: translateX(-50px);
     179    transition: opacity 0.6s ease-out, transform 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
     180}
     181
     182body:not(.block-editor-page) .scroll-bounce-in-left.fade-in-visible {
     183    opacity: 1;
     184    transform: translateX(0);
     185}
     186
     187/* Bounce-In from right */
     188body:not(.block-editor-page) .scroll-bounce-in-right {
     189    opacity: 0;
     190    transform: translateX(50px);
     191    transition: opacity 0.6s ease-out, transform 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
     192}
     193
     194body:not(.block-editor-page) .scroll-bounce-in-right.fade-in-visible {
     195    opacity: 1;
     196    transform: translateX(0);
     197}
     198
     199/* Bounce-In from top */
     200body:not(.block-editor-page) .scroll-bounce-in-top {
     201    opacity: 0;
     202    transform: translateY(-50px);
     203    transition: opacity 0.6s ease-out, transform 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
     204}
     205
     206body:not(.block-editor-page) .scroll-bounce-in-top.fade-in-visible {
     207    opacity: 1;
     208    transform: translateY(0);
     209}
     210
     211/* ============================================
     212   FLIP ANIMATIONS
     213   ============================================ */
     214
     215/* Flip-In from bottom (default - rotateX) */
     216body:not(.block-editor-page) .scroll-flip-in {
     217    opacity: 0;
     218    transform: perspective(400px) rotateX(90deg);
     219    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     220}
     221
     222body:not(.block-editor-page) .scroll-flip-in.fade-in-visible {
     223    opacity: 1;
     224    transform: perspective(400px) rotateX(0deg);
     225}
     226
     227/* Flip-In from left (rotateY) */
     228body:not(.block-editor-page) .scroll-flip-in-left {
     229    opacity: 0;
     230    transform: perspective(400px) rotateY(-90deg);
     231    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     232}
     233
     234body:not(.block-editor-page) .scroll-flip-in-left.fade-in-visible {
     235    opacity: 1;
     236    transform: perspective(400px) rotateY(0deg);
     237}
     238
     239/* Flip-In from right (rotateY) */
     240body:not(.block-editor-page) .scroll-flip-in-right {
    91241    opacity: 0;
    92242    transform: perspective(400px) rotateY(90deg);
     
    94244}
    95245
    96 .scroll-flip-in.fade-in-visible {
     246body:not(.block-editor-page) .scroll-flip-in-right.fade-in-visible {
    97247    opacity: 1;
    98248    transform: perspective(400px) rotateY(0deg);
     249}
     250
     251/* Flip-In from top (rotateX) */
     252body:not(.block-editor-page) .scroll-flip-in-top {
     253    opacity: 0;
     254    transform: perspective(400px) rotateX(-90deg);
     255    transition: opacity 0.8s ease-out, transform 0.8s ease-out;
     256}
     257
     258body:not(.block-editor-page) .scroll-flip-in-top.fade-in-visible {
     259    opacity: 1;
     260    transform: perspective(400px) rotateX(0deg);
     261}
     262
     263/* ============================================
     264   SLOW MODIFIER
     265   Macht alle Animationen langsamer (1.5x)
     266   ============================================ */
     267
     268body:not(.block-editor-page) [class*="scroll-"].animation-slow {
     269    transition-duration: 1.2s !important;
     270}
     271
     272body:not(.block-editor-page) .scroll-bounce-in.animation-slow,
     273body:not(.block-editor-page) .scroll-bounce-in-left.animation-slow,
     274body:not(.block-editor-page) .scroll-bounce-in-right.animation-slow,
     275body:not(.block-editor-page) .scroll-bounce-in-top.animation-slow {
     276    transition-duration: 0.9s !important;
    99277}
    100278
     
    124302
    125303/* Performance optimizations */
    126 .scroll-fade-in,
    127 .scroll-fade-in-left,
    128 .scroll-fade-in-right,
    129 .scroll-fade-in-slow,
    130 .scroll-zoom-in,
    131 .scroll-rotate-in,
    132 .scroll-bounce-in,
    133 .scroll-flip-in {
     304[class*="scroll-fade-"],
     305[class*="scroll-zoom-"],
     306[class*="scroll-rotate-"],
     307[class*="scroll-bounce-"],
     308[class*="scroll-flip-"] {
    134309    will-change: opacity, transform;
    135310}
     
    137312/* Respect reduced motion */
    138313@media (prefers-reduced-motion: reduce) {
    139     .scroll-fade-in,
    140     .scroll-fade-in-left,
    141     .scroll-fade-in-right,
    142     .scroll-fade-in-slow,
    143     .scroll-zoom-in,
    144     .scroll-rotate-in,
    145     .scroll-bounce-in,
    146     .scroll-flip-in {
     314    [class*="scroll-fade-"],
     315    [class*="scroll-zoom-"],
     316    [class*="scroll-rotate-"],
     317    [class*="scroll-bounce-"],
     318    [class*="scroll-flip-"] {
    147319        transition: opacity 0.3s ease-out;
    148320        transition-delay: 0s !important;
     
    150322    }
    151323
    152     .scroll-fade-in.fade-in-visible,
    153     .scroll-fade-in-left.fade-in-visible,
    154     .scroll-fade-in-right.fade-in-visible,
    155     .scroll-fade-in-slow.fade-in-visible,
    156     .scroll-zoom-in.fade-in-visible,
    157     .scroll-rotate-in.fade-in-visible,
    158     .scroll-bounce-in.fade-in-visible,
    159     .scroll-flip-in.fade-in-visible {
     324    [class*="scroll-fade-"].fade-in-visible,
     325    [class*="scroll-zoom-"].fade-in-visible,
     326    [class*="scroll-rotate-"].fade-in-visible,
     327    [class*="scroll-bounce-"].fade-in-visible,
     328    [class*="scroll-flip-"].fade-in-visible {
    160329        opacity: 1;
    161330        transform: none !important;
    162331    }
    163332}
     333
     334/* Editor Styles - Make animations visible and triggerable in Gutenberg */
     335/* Override frontend styles completely to keep elements visible */
     336/* Only apply when NOT animating (no .serend-preview-animate class) */
     337.block-editor [class*="scroll-"]:not(.serend-preview-animate),
     338.block-editor-block-list__layout [class*="scroll-"]:not(.serend-preview-animate) {
     339    /* Keep elements visible in editor - override frontend opacity: 0 */
     340    opacity: 1 !important;
     341    transform: none !important;
     342    visibility: visible !important;
     343    transition: none !important;
     344}
     345
     346
     347/* Animation classes for editor preview */
     348/* These animations are triggered when the user clicks "Show Animation" */
     349
     350/* Fade animations */
     351.block-editor .scroll-fade-in.serend-preview-animate,
     352.block-editor-block-list__layout .scroll-fade-in.serend-preview-animate {
     353    animation: fadeInUp 0.8s ease-out forwards;
     354}
     355.block-editor .scroll-fade-in-left.serend-preview-animate,
     356.block-editor-block-list__layout .scroll-fade-in-left.serend-preview-animate {
     357    animation: fadeInLeft 0.8s ease-out forwards;
     358}
     359.block-editor .scroll-fade-in-right.serend-preview-animate,
     360.block-editor-block-list__layout .scroll-fade-in-right.serend-preview-animate {
     361    animation: fadeInRight 0.8s ease-out forwards;
     362}
     363.block-editor .scroll-fade-in-top.serend-preview-animate,
     364.block-editor-block-list__layout .scroll-fade-in-top.serend-preview-animate {
     365    animation: fadeInTop 0.8s ease-out forwards;
     366}
     367
     368/* Zoom animations */
     369.block-editor .scroll-zoom-in.serend-preview-animate,
     370.block-editor-block-list__layout .scroll-zoom-in.serend-preview-animate {
     371    animation: zoomInUp 0.8s ease-out forwards;
     372}
     373.block-editor .scroll-zoom-in-left.serend-preview-animate,
     374.block-editor-block-list__layout .scroll-zoom-in-left.serend-preview-animate {
     375    animation: zoomInLeft 0.8s ease-out forwards;
     376}
     377.block-editor .scroll-zoom-in-right.serend-preview-animate,
     378.block-editor-block-list__layout .scroll-zoom-in-right.serend-preview-animate {
     379    animation: zoomInRight 0.8s ease-out forwards;
     380}
     381.block-editor .scroll-zoom-in-top.serend-preview-animate,
     382.block-editor-block-list__layout .scroll-zoom-in-top.serend-preview-animate {
     383    animation: zoomInTop 0.8s ease-out forwards;
     384}
     385
     386/* Rotate animations */
     387.block-editor .scroll-rotate-in.serend-preview-animate,
     388.block-editor-block-list__layout .scroll-rotate-in.serend-preview-animate {
     389    animation: rotateInUp 0.8s ease-out forwards;
     390}
     391.block-editor .scroll-rotate-in-left.serend-preview-animate,
     392.block-editor-block-list__layout .scroll-rotate-in-left.serend-preview-animate {
     393    animation: rotateInLeft 0.8s ease-out forwards;
     394}
     395.block-editor .scroll-rotate-in-right.serend-preview-animate,
     396.block-editor-block-list__layout .scroll-rotate-in-right.serend-preview-animate {
     397    animation: rotateInRight 0.8s ease-out forwards;
     398}
     399.block-editor .scroll-rotate-in-top.serend-preview-animate,
     400.block-editor-block-list__layout .scroll-rotate-in-top.serend-preview-animate {
     401    animation: rotateInTop 0.8s ease-out forwards;
     402}
     403
     404/* Bounce animations */
     405.block-editor .scroll-bounce-in.serend-preview-animate,
     406.block-editor-block-list__layout .scroll-bounce-in.serend-preview-animate {
     407    animation: bounceInUp 0.8s ease-out forwards;
     408}
     409.block-editor .scroll-bounce-in-left.serend-preview-animate,
     410.block-editor-block-list__layout .scroll-bounce-in-left.serend-preview-animate {
     411    animation: bounceInLeft 0.8s ease-out forwards;
     412}
     413.block-editor .scroll-bounce-in-right.serend-preview-animate,
     414.block-editor-block-list__layout .scroll-bounce-in-right.serend-preview-animate {
     415    animation: bounceInRight 0.8s ease-out forwards;
     416}
     417.block-editor .scroll-bounce-in-top.serend-preview-animate,
     418.block-editor-block-list__layout .scroll-bounce-in-top.serend-preview-animate {
     419    animation: bounceInTop 0.8s ease-out forwards;
     420}
     421
     422/* Flip animations */
     423.block-editor .scroll-flip-in.serend-preview-animate,
     424.block-editor-block-list__layout .scroll-flip-in.serend-preview-animate {
     425    animation: flipInUp 0.8s ease-out forwards;
     426}
     427.block-editor .scroll-flip-in-left.serend-preview-animate,
     428.block-editor-block-list__layout .scroll-flip-in-left.serend-preview-animate {
     429    animation: flipInLeft 0.8s ease-out forwards;
     430}
     431.block-editor .scroll-flip-in-right.serend-preview-animate,
     432.block-editor-block-list__layout .scroll-flip-in-right.serend-preview-animate {
     433    animation: flipInRight 0.8s ease-out forwards;
     434}
     435.block-editor .scroll-flip-in-top.serend-preview-animate,
     436.block-editor-block-list__layout .scroll-flip-in-top.serend-preview-animate {
     437    animation: flipInTop 0.8s ease-out forwards;
     438}
     439
     440/* Slow modifier for editor animations */
     441.block-editor .animation-slow.serend-preview-animate,
     442.block-editor-block-list__layout .animation-slow.serend-preview-animate {
     443    animation-duration: 1.2s !important;
     444}
     445
     446/* Animation keyframes for editor preview */
     447
     448/* Fade keyframes */
     449@keyframes fadeInUp {
     450    from { opacity: 0; transform: translateY(30px); }
     451    to { opacity: 1; transform: translateY(0); }
     452}
     453@keyframes fadeInLeft {
     454    from { opacity: 0; transform: translateX(-30px); }
     455    to { opacity: 1; transform: translateX(0); }
     456}
     457@keyframes fadeInRight {
     458    from { opacity: 0; transform: translateX(30px); }
     459    to { opacity: 1; transform: translateX(0); }
     460}
     461@keyframes fadeInTop {
     462    from { opacity: 0; transform: translateY(-30px); }
     463    to { opacity: 1; transform: translateY(0); }
     464}
     465
     466/* Zoom keyframes */
     467@keyframes zoomInUp {
     468    from { opacity: 0; transform: scale(0.8) translateY(30px); }
     469    to { opacity: 1; transform: scale(1) translateY(0); }
     470}
     471@keyframes zoomInLeft {
     472    from { opacity: 0; transform: scale(0.8) translateX(-30px); }
     473    to { opacity: 1; transform: scale(1) translateX(0); }
     474}
     475@keyframes zoomInRight {
     476    from { opacity: 0; transform: scale(0.8) translateX(30px); }
     477    to { opacity: 1; transform: scale(1) translateX(0); }
     478}
     479@keyframes zoomInTop {
     480    from { opacity: 0; transform: scale(0.8) translateY(-30px); }
     481    to { opacity: 1; transform: scale(1) translateY(0); }
     482}
     483
     484/* Rotate keyframes */
     485@keyframes rotateInUp {
     486    from { opacity: 0; transform: rotate(-10deg) scale(0.9) translateY(30px); }
     487    to { opacity: 1; transform: rotate(0deg) scale(1) translateY(0); }
     488}
     489@keyframes rotateInLeft {
     490    from { opacity: 0; transform: rotate(-10deg) scale(0.9) translateX(-30px); }
     491    to { opacity: 1; transform: rotate(0deg) scale(1) translateX(0); }
     492}
     493@keyframes rotateInRight {
     494    from { opacity: 0; transform: rotate(10deg) scale(0.9) translateX(30px); }
     495    to { opacity: 1; transform: rotate(0deg) scale(1) translateX(0); }
     496}
     497@keyframes rotateInTop {
     498    from { opacity: 0; transform: rotate(-10deg) scale(0.9) translateY(-30px); }
     499    to { opacity: 1; transform: rotate(0deg) scale(1) translateY(0); }
     500}
     501
     502/* Bounce keyframes */
     503@keyframes bounceInUp {
     504    from { opacity: 0; transform: translateY(50px); }
     505    to { opacity: 1; transform: translateY(0); }
     506}
     507@keyframes bounceInLeft {
     508    from { opacity: 0; transform: translateX(-50px); }
     509    to { opacity: 1; transform: translateX(0); }
     510}
     511@keyframes bounceInRight {
     512    from { opacity: 0; transform: translateX(50px); }
     513    to { opacity: 1; transform: translateX(0); }
     514}
     515@keyframes bounceInTop {
     516    from { opacity: 0; transform: translateY(-50px); }
     517    to { opacity: 1; transform: translateY(0); }
     518}
     519
     520/* Flip keyframes */
     521@keyframes flipInUp {
     522    from { opacity: 0; transform: perspective(400px) rotateX(90deg); }
     523    to { opacity: 1; transform: perspective(400px) rotateX(0deg); }
     524}
     525@keyframes flipInLeft {
     526    from { opacity: 0; transform: perspective(400px) rotateY(-90deg); }
     527    to { opacity: 1; transform: perspective(400px) rotateY(0deg); }
     528}
     529@keyframes flipInRight {
     530    from { opacity: 0; transform: perspective(400px) rotateY(90deg); }
     531    to { opacity: 1; transform: perspective(400px) rotateY(0deg); }
     532}
     533@keyframes flipInTop {
     534    from { opacity: 0; transform: perspective(400px) rotateX(-90deg); }
     535    to { opacity: 1; transform: perspective(400px) rotateX(0deg); }
     536}
  • serend-animations/trunk/includes/assets/js/animations.js

    r3368477 r3390988  
    1414    function initScrollAnimations() {
    1515        // Find all elements with animation classes.
    16         const animationSelectors = [
    17             '.scroll-fade-in',
    18             '.scroll-fade-in-left',
    19             '.scroll-fade-in-right',
    20             '.scroll-fade-in-slow',
    21             '.scroll-zoom-in',
    22             '.scroll-rotate-in',
    23             '.scroll-bounce-in',
    24             '.scroll-flip-in'
    25         ];
    26 
    27         const animationElements = document.querySelectorAll( animationSelectors.join( ', ' ) );
     16        // Use attribute selector to catch all scroll-* classes (more flexible and future-proof).
     17        const animationElements = document.querySelectorAll( '[class*="scroll-fade-"], [class*="scroll-zoom-"], [class*="scroll-rotate-"], [class*="scroll-bounce-"], [class*="scroll-flip-"]' );
    2818
    2919        // If no elements were found, exit the function.
     
    6858        } else {
    6959            // Fallback for older browsers - show all elements immediately.
    70             const animationElements = document.querySelectorAll( [
    71                 '.scroll-fade-in',
    72                 '.scroll-fade-in-left',
    73                 '.scroll-fade-in-right',
    74                 '.scroll-fade-in-slow',
    75                 '.scroll-zoom-in',
    76                 '.scroll-rotate-in',
    77                 '.scroll-bounce-in',
    78                 '.scroll-flip-in'
    79             ].join( ', ' ) );
     60            const animationElements = document.querySelectorAll( '[class*="scroll-fade-"], [class*="scroll-zoom-"], [class*="scroll-rotate-"], [class*="scroll-bounce-"], [class*="scroll-flip-"]' );
    8061
    8162            animationElements.forEach( element => {
     
    11091
    11192        window.debugAnimations = function() {
    112             const elements = document.querySelectorAll( [
    113                 '.scroll-fade-in',
    114                 '.scroll-fade-in-left',
    115                 '.scroll-fade-in-right',
    116                 '.scroll-fade-in-slow',
    117                 '.scroll-zoom-in',
    118                 '.scroll-rotate-in',
    119                 '.scroll-bounce-in',
    120                 '.scroll-flip-in'
    121             ].join( ', ' ) );
     93            const elements = document.querySelectorAll( '[class*="scroll-fade-"], [class*="scroll-zoom-"], [class*="scroll-rotate-"], [class*="scroll-bounce-"], [class*="scroll-flip-"]' );
    12294
    12395            console.log( 'Animation elements found:', elements.length );
  • serend-animations/trunk/includes/assets/js/editor.js

    r3368477 r3390988  
    1111    const { __, _x, _n, _nx } = wp.i18n;
    1212    const { addFilter } = wp.hooks;
    13     const { Fragment } = wp.element;
     13    const { Fragment, useState, useEffect } = wp.element;
    1414    const { InspectorControls } = wp.blockEditor;
    15     const { PanelBody, SelectControl, RangeControl } = wp.components;
     15    const { PanelBody, SelectControl, RangeControl, Button, ToggleControl } = wp.components;
    1616    const { createHigherOrderComponent } = wp.compose;
    1717
    1818    /**
    19      * Add animation attributes to all blocks.
    20      */
    21     function addAnimationAttribute( settings, name ) {
    22         // Skip certain core blocks that don't need animations.
    23         const skipBlocks = [
    24             'core/html',
    25             'core/shortcode',
    26             'core/spacer',
    27             'core/more',
    28             'core/nextpage'
     19     * Extract animation class from className string.
     20     *
     21     * @param {string} className The className string to parse.
     22     * @return {string} The animation class found, or empty string.
     23     */
     24    function extractAnimationClass( className ) {
     25        if ( ! className || typeof className !== 'string' ) {
     26            return '';
     27        }
     28
     29        // Split className into array of classes
     30        const classes = className.split( /\s+/ ).filter( cls => cls.length > 0 );
     31
     32        // All possible animation base classes with their direction variants.
     33        const animationPrefixes = [
     34            'scroll-flip-in',
     35            'scroll-bounce-in',
     36            'scroll-rotate-in',
     37            'scroll-zoom-in',
     38            'scroll-fade-in'
    2939        ];
    3040
    31         if ( skipBlocks.includes( name ) ) {
    32             return settings;
    33         }
    34 
    35         // Add animation attributes.
    36         if ( ! settings.attributes ) {
    37             settings.attributes = {};
    38         }
    39 
    40         settings.attributes.animationClass = {
    41             type: 'string',
    42             default: ''
    43         };
    44 
    45         settings.attributes.animationDelay = {
    46             type: 'number',
    47             default: 0
    48         };
    49 
    50         return settings;
     41        // Check each prefix (longest first to match most specific).
     42        for ( let i = 0; i < animationPrefixes.length; i++ ) {
     43            const prefix = animationPrefixes[ i ];
     44           
     45            // Check for direction variants first.
     46            if ( classes.includes( prefix + '-left' ) ) {
     47                return prefix + '-left';
     48            }
     49            if ( classes.includes( prefix + '-right' ) ) {
     50                return prefix + '-right';
     51            }
     52            if ( classes.includes( prefix + '-top' ) ) {
     53                return prefix + '-top';
     54            }
     55            // Check for base class.
     56            if ( classes.includes( prefix ) ) {
     57                return prefix;
     58            }
     59        }
     60
     61        return '';
     62    }
     63
     64    /**
     65     * Extract animation type from animation class.
     66     *
     67     * @param {string} animationClass The animation class (e.g., 'scroll-fade-in-left').
     68     * @return {string} The animation type (e.g., 'fade', 'zoom').
     69     */
     70    function extractAnimationType( animationClass ) {
     71        if ( ! animationClass ) {
     72            return '';
     73        }
     74
     75        // Map animation classes to types.
     76        if ( animationClass.startsWith( 'scroll-fade-in' ) ) {
     77            return 'fade';
     78        }
     79        if ( animationClass.startsWith( 'scroll-zoom-in' ) ) {
     80            return 'zoom';
     81        }
     82        if ( animationClass.startsWith( 'scroll-rotate-in' ) ) {
     83            return 'rotate';
     84        }
     85        if ( animationClass.startsWith( 'scroll-bounce-in' ) ) {
     86            return 'bounce';
     87        }
     88        if ( animationClass.startsWith( 'scroll-flip-in' ) ) {
     89            return 'flip';
     90        }
     91
     92        return '';
     93    }
     94
     95    /**
     96     * Extract animation direction from animation class.
     97     *
     98     * @param {string} animationClass The animation class (e.g., 'scroll-fade-in-left').
     99     * @return {string} The direction ('top', 'bottom', 'left', 'right').
     100     */
     101    function extractAnimationDirection( animationClass ) {
     102        if ( ! animationClass ) {
     103            return 'bottom';
     104        }
     105
     106        if ( animationClass.includes( '-left' ) ) {
     107            return 'left';
     108        }
     109        if ( animationClass.includes( '-right' ) ) {
     110            return 'right';
     111        }
     112        if ( animationClass.includes( '-top' ) ) {
     113            return 'top';
     114        }
     115
     116        // Default is bottom.
     117        return 'bottom';
     118    }
     119
     120    /**
     121     * Build animation class from type and direction.
     122     *
     123     * @param {string} type The animation type (e.g., 'fade', 'zoom').
     124     * @param {string} direction The direction ('top', 'bottom', 'left', 'right').
     125     * @return {string} The complete animation class.
     126     */
     127    function buildAnimationClass( type, direction ) {
     128        if ( ! type || type === '' ) {
     129            return '';
     130        }
     131
     132        // Build class based on type and direction.
     133        let baseClass = '';
     134       
     135        if ( type === 'fade' ) {
     136            baseClass = 'scroll-fade-in';
     137        } else if ( type === 'zoom' ) {
     138            baseClass = 'scroll-zoom-in';
     139        } else if ( type === 'rotate' ) {
     140            baseClass = 'scroll-rotate-in';
     141        } else if ( type === 'bounce' ) {
     142            baseClass = 'scroll-bounce-in';
     143        } else if ( type === 'flip' ) {
     144            baseClass = 'scroll-flip-in';
     145        }
     146
     147        if ( ! baseClass ) {
     148            return '';
     149        }
     150
     151        // Add direction suffix if not bottom.
     152        if ( direction === 'left' ) {
     153            return baseClass + '-left';
     154        }
     155        if ( direction === 'right' ) {
     156            return baseClass + '-right';
     157        }
     158        if ( direction === 'top' ) {
     159            return baseClass + '-top';
     160        }
     161
     162        // Default: bottom (no suffix).
     163        return baseClass;
     164    }
     165
     166    /**
     167     * Extract slow modifier from className string.
     168     *
     169     * @param {string} className The className string to parse.
     170     * @return {boolean} True if animation-slow class is present.
     171     */
     172    function extractSlowModifier( className ) {
     173        if ( ! className || typeof className !== 'string' ) {
     174            return false;
     175        }
     176
     177        return className.includes( 'animation-slow' );
     178    }
     179
     180    /**
     181     * Extract animation delay from className string.
     182     *
     183     * @param {string} className The className string to parse.
     184     * @return {number} The delay value found, or 0.
     185     */
     186    function extractAnimationDelay( className ) {
     187        if ( ! className || typeof className !== 'string' ) {
     188            return 0;
     189        }
     190
     191        // Match animation-delay-{number} pattern.
     192        const delayMatch = className.match( /\banimation-delay-(\d+)\b/ );
     193        if ( delayMatch && delayMatch[ 1 ] ) {
     194            const delay = parseInt( delayMatch[ 1 ], 10 );
     195            return isNaN( delay ) ? 0 : delay;
     196        }
     197
     198        return 0;
    51199    }
    52200
     
    56204    const withAnimationControl = createHigherOrderComponent( ( BlockEdit ) => {
    57205        return ( props ) => {
    58             const { attributes, setAttributes, name } = props;
    59             const { animationClass, animationDelay, className } = attributes;
    60 
    61             // Skip certain core blocks that don't need animations.
    62             const skipBlocks = [
    63                 'core/html',
    64                 'core/shortcode',
    65                 'core/spacer',
    66                 'core/more',
    67                 'core/nextpage'
    68             ];
    69 
    70             if ( skipBlocks.includes( name ) ) {
     206            // Ensure props is valid.
     207            if ( ! props || typeof props !== 'object' ) {
    71208                return wp.element.createElement( BlockEdit, props );
    72209            }
    73210
    74             const animationOptions = Object.keys( serendAnimations.animations ).map( key => ( {
    75                 label: serendAnimations.animations[ key ],
     211            const { attributes, setAttributes, name, clientId } = props;
     212
     213            // Ensure attributes exists and is an object.
     214            if ( ! attributes || typeof attributes !== 'object' ) {
     215                return wp.element.createElement( BlockEdit, props );
     216            }
     217
     218            // Ensure setAttributes is a function.
     219            if ( ! setAttributes || typeof setAttributes !== 'function' ) {
     220                return wp.element.createElement( BlockEdit, props );
     221            }
     222
     223            // Get className - this is the only attribute we need.
     224            // Note: Even if className is not in attributes, WordPress may add it automatically.
     225            const className = attributes.className || '';
     226
     227            // Ensure serendAnimations exists and has animationTypes.
     228            if ( typeof serendAnimations === 'undefined' || ! serendAnimations.animationTypes || typeof serendAnimations.animationTypes !== 'object' ) {
     229                return wp.element.createElement( BlockEdit, props );
     230            }
     231
     232            const animationTypeOptions = Object.keys( serendAnimations.animationTypes ).map( key => ( {
     233                label: serendAnimations.animationTypes[ key ],
    76234                value: key
    77235            } ) );
    78236
    79             // Function to set animation and delay.
    80             const updateAnimationClasses = ( newAnimationClass, newDelay ) => {
    81                 let newClassName = className || '';
    82 
    83                 // Remove all existing animation classes and delay classes.
    84                 const animationClasses = Object.keys( serendAnimations.animations ).filter( key => key !== '' );
    85                 animationClasses.forEach( animClass => {
    86                     newClassName = newClassName.replace( new RegExp( '\\b' + animClass.replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' ) + '\\b', 'g' ), '' ).trim();
     237            // Extract current animation values from className.
     238            const animationClass = extractAnimationClass( className );
     239            const animationType = extractAnimationType( animationClass );
     240            const animationDirection = extractAnimationDirection( animationClass );
     241            const animationSlow = extractSlowModifier( className );
     242            const animationDelay = extractAnimationDelay( className );
     243
     244            // State for animation preview.
     245            const [ isAnimating, setIsAnimating ] = useState( false );
     246
     247            // Function to get the editor document (handles iframe).
     248            const getEditorDocument = () => {
     249                // Try to find the editor canvas iframe.
     250                const iframe = document.querySelector( 'iframe[name="editor-canvas"]' );
     251                if ( iframe && iframe.contentDocument ) {
     252                    return iframe.contentDocument;
     253                }
     254                // Fallback to current document.
     255                return document;
     256            };
     257
     258            // Function to trigger animation preview in editor.
     259            const triggerAnimationPreview = () => {
     260                if ( ! animationClass || animationClass === '' || ! clientId ) {
     261                    return;
     262                }
     263
     264                // Get the correct document (might be in iframe).
     265                const editorDoc = getEditorDocument();
     266
     267                // Find the block element in the editor.
     268                // Use data-block attribute to find the correct block.
     269                let blockElement = editorDoc.querySelector( `[data-block="${ clientId }"]` );
     270               
     271                // If not found, try without iframe.
     272                if ( ! blockElement && editorDoc !== document ) {
     273                    blockElement = document.querySelector( `[data-block="${ clientId }"]` );
     274                }
     275
     276                if ( ! blockElement ) {
     277                    return;
     278                }
     279
     280                // Always animate the blockElement itself.
     281                const targetElement = blockElement;
     282
     283                if ( targetElement ) {
     284                    // Set animating state.
     285                    setIsAnimating( true );
     286
     287                    // Add the animation class if not present
     288                    if ( ! targetElement.classList.contains( animationClass ) ) {
     289                        targetElement.classList.add( animationClass );
     290                    }
     291
     292                    // Remove the preview animate class first to reset.
     293                    targetElement.classList.remove( 'serend-preview-animate' );
     294                   
     295                    // Force reflow to restart animation.
     296                    void targetElement.offsetHeight;
     297                   
     298                    // Add the preview animate class to trigger animation.
     299                    requestAnimationFrame( () => {
     300                        targetElement.classList.add( 'serend-preview-animate' );
     301
     302                        // Remove the class after animation completes.
     303                        setTimeout( () => {
     304                            if ( targetElement ) {
     305                                targetElement.classList.remove( 'serend-preview-animate' );
     306                            }
     307                            setIsAnimating( false );
     308                        }, 1500 ); // Max animation duration + buffer.
     309                    } );
     310                }
     311            };
     312
     313            // Function to set animation, slow modifier and delay.
     314            const updateAnimationClasses = ( newAnimationClass, newSlow, newDelay ) => {
     315                // Ensure newAnimationClass is a string.
     316                const safeAnimationClass = typeof newAnimationClass === 'string' ? newAnimationClass : '';
     317                // Ensure newSlow is a boolean.
     318                const safeSlow = typeof newSlow === 'boolean' ? newSlow : false;
     319                // Ensure newDelay is a number.
     320                const safeDelay = typeof newDelay === 'number' && ! isNaN( newDelay ) ? Math.max( 0, newDelay ) : 0;
     321
     322                // Get current className from attributes to ensure we have the latest value.
     323                const currentClassName = ( attributes && typeof attributes.className === 'string' ) ? attributes.className : '';
     324               
     325                // Split className into array and filter out animation-related classes
     326                let classArray = currentClassName.split( /\s+/ ).filter( cls => cls.length > 0 );
     327
     328                // Remove all animation classes (scroll-*)
     329                classArray = classArray.filter( cls => ! cls.startsWith( 'scroll-' ) );
     330
     331                // Remove animation-slow class
     332                classArray = classArray.filter( cls => cls !== 'animation-slow' );
     333
     334                // Remove all delay classes (animation-delay-*)
     335                classArray = classArray.filter( cls => ! cls.startsWith( 'animation-delay-' ) );
     336
     337                // Remove orphaned direction classes (-left, -right, -slow, etc.)
     338                classArray = classArray.filter( cls => {
     339                    // These are incomplete class fragments that should be removed
     340                    return cls !== '-left' &&
     341                           cls !== '-right' &&
     342                           cls !== '-top' &&
     343                           cls !== '-slow' &&
     344                           cls !== '-in' &&
     345                           cls !== '-in-left' &&
     346                           cls !== '-in-right' &&
     347                           cls !== '-in-top';
    87348                } );
    88349
    89                 // Remove old delay classes.
    90                 newClassName = newClassName.replace( /\banimation-delay-\d+\b/g, '' ).trim();
    91 
    92350                // Add new animation class (if not empty).
    93                 if ( newAnimationClass && newAnimationClass !== '' ) {
    94                     newClassName = newClassName ? newClassName + ' ' + newAnimationClass : newAnimationClass;
     351                if ( safeAnimationClass && safeAnimationClass !== '' ) {
     352                    classArray.push( safeAnimationClass );
     353
     354                    // Add slow modifier if enabled.
     355                    if ( safeSlow ) {
     356                        classArray.push( 'animation-slow' );
     357                    }
    95358
    96359                    // Add delay class if > 0.
    97                     if ( newDelay > 0 ) {
    98                         newClassName += ' animation-delay-' + newDelay;
     360                    if ( safeDelay > 0 ) {
     361                        classArray.push( 'animation-delay-' + safeDelay );
    99362                    }
    100363                }
    101364
    102                 // Clean up whitespace.
    103                 newClassName = newClassName.replace( /\s+/g, ' ' ).trim();
    104 
    105                 // Set attributes.
    106                 setAttributes( {
    107                     animationClass: newAnimationClass,
    108                     animationDelay: newDelay,
    109                     className: newClassName || undefined
    110                 } );
    111             };
    112 
    113             // Handler for animation change.
    114             const setAnimationClass = ( newAnimationClass ) => {
    115                 updateAnimationClasses( newAnimationClass, animationDelay );
     365                // Join classes and clean up whitespace
     366                const newClassName = classArray.join( ' ' ).trim();
     367
     368                // Set className attribute safely (this works for all blocks that support className).
     369                try {
     370                    // Only update className if it changed.
     371                    if ( newClassName !== currentClassName ) {
     372                        setAttributes( {
     373                            className: newClassName || undefined
     374                        } );
     375                    }
     376                } catch ( e ) {
     377                    // Silently fail if setAttributes doesn't work.
     378                }
     379            };
     380
     381            // Handler for animation type change.
     382            const setAnimationType = ( newType ) => {
     383                // Keep current direction.
     384                const newAnimationClass = buildAnimationClass( newType, animationDirection );
     385                updateAnimationClasses( newAnimationClass, animationSlow, animationDelay );
     386            };
     387
     388            // Handler for direction change.
     389            const setAnimationDirection = ( newDirection ) => {
     390                const newAnimationClass = buildAnimationClass( animationType, newDirection );
     391                updateAnimationClasses( newAnimationClass, animationSlow, animationDelay );
     392            };
     393
     394            // Handler for slow toggle change.
     395            const setAnimationSlow = ( newSlow ) => {
     396                updateAnimationClasses( animationClass, newSlow, animationDelay );
    116397            };
    117398
    118399            // Handler for delay change.
    119400            const setAnimationDelay = ( newDelay ) => {
    120                 updateAnimationClasses( animationClass, newDelay );
     401                updateAnimationClasses( animationClass, animationSlow, newDelay );
     402            };
     403
     404            // Auto-trigger animation preview when animation or slow changes.
     405            // Note: Delay is intentionally excluded - it should only affect frontend, not editor preview.
     406            useEffect( () => {
     407                // Only trigger if an animation is selected.
     408                if ( animationClass && animationClass !== '' ) {
     409                    // Trigger animation immediately.
     410                    triggerAnimationPreview();
     411                }
     412            }, [ animationClass, animationSlow ] );
     413
     414            // Create direction button helper.
     415            const createDirectionButton = ( direction, rotation ) => {
     416                const isActive = animationDirection === direction;
     417                return wp.element.createElement(
     418                    Button,
     419                    {
     420                        variant: isActive ? 'primary' : 'secondary',
     421                        className: 'serend-direction-button',
     422                        onClick: () => setAnimationDirection( direction ),
     423                        style: {
     424                            flex: 1,
     425                            display: 'flex',
     426                            alignItems: 'center',
     427                            justifyContent: 'center',
     428                            minHeight: '36px',
     429                            padding: '8px',
     430                            backgroundColor: isActive ? '#000' : 'transparent',
     431                            color: isActive ? '#fff' : '#000'
     432                        }
     433                    },
     434                    wp.element.createElement(
     435                        'svg',
     436                        {
     437                            xmlns: 'http://www.w3.org/2000/svg',
     438                            width: '20',
     439                            height: '20',
     440                            viewBox: '0 0 24 24',
     441                            fill: 'none',
     442                            stroke: 'currentColor',
     443                            strokeWidth: '2',
     444                            strokeLinecap: 'round',
     445                            strokeLinejoin: 'round',
     446                            style: { transform: `rotate(${ rotation }deg)` }
     447                        },
     448                        wp.element.createElement( 'path', { d: 'm9 6-6 6 6 6' } ),
     449                        wp.element.createElement( 'path', { d: 'M3 12h14' } ),
     450                        wp.element.createElement( 'path', { d: 'M21 19V5' } )
     451                    )
     452                );
    121453            };
    122454
     
    151483                            initialOpen: true
    152484                        },
     485                        // Animation Type Dropdown
    153486                        wp.element.createElement( SelectControl, {
    154                             label: ( typeof serendAnimations !== 'undefined' && serendAnimations.i18n ) ? serendAnimations.i18n.scrollAnimation : 'Scroll Animation',
    155                             value: animationClass || '',
    156                             options: animationOptions,
    157                             onChange: setAnimationClass,
     487                            label: ( typeof serendAnimations !== 'undefined' && serendAnimations.i18n ) ? serendAnimations.i18n.animationType : 'Animation Type',
     488                            value: animationType || '',
     489                            options: animationTypeOptions,
     490                            onChange: setAnimationType,
    158491                            help: ( typeof serendAnimations !== 'undefined' && serendAnimations.i18n ) ? serendAnimations.i18n.animationHelp : 'Choose an animation that triggers when scrolling.'
    159492                        } ),
    160                         animationClass && animationClass !== '' && wp.element.createElement( RangeControl, {
     493                        // Direction Buttons (show when animation is selected)
     494                        animationType && animationType !== '' && wp.element.createElement(
     495                            'div',
     496                            { style: { marginBottom: '16px' } },
     497                            wp.element.createElement(
     498                                'div',
     499                                {
     500                                    style: {
     501                                        marginBottom: '8px',
     502                                        fontSize: '11px',
     503                                        fontWeight: '500',
     504                                        textTransform: 'uppercase',
     505                                        color: '#1e1e1e'
     506                                    }
     507                                },
     508                                ( typeof serendAnimations !== 'undefined' && serendAnimations.i18n ) ? serendAnimations.i18n.directionLabel : 'Direction'
     509                            ),
     510                            wp.element.createElement(
     511                                'div',
     512                                {
     513                                    style: {
     514                                        display: 'grid',
     515                                        gridTemplateColumns: 'repeat(4, 1fr)',
     516                                        gap: '8px'
     517                                    }
     518                                },
     519                                createDirectionButton( 'top', -90 ),
     520                                createDirectionButton( 'right', 0 ),
     521                                createDirectionButton( 'bottom', 90 ),
     522                                createDirectionButton( 'left', 180 )
     523                            ),
     524                            wp.element.createElement(
     525                                'p',
     526                                {
     527                                    style: {
     528                                        marginTop: '8px',
     529                                        marginBottom: 0,
     530                                        fontSize: '12px',
     531                                        fontStyle: 'normal',
     532                                        color: '#757575'
     533                                    }
     534                                },
     535                                ( typeof serendAnimations !== 'undefined' && serendAnimations.i18n ) ? serendAnimations.i18n.directionHelp : 'Select the direction from which the animation starts.'
     536                            )
     537                        ),
     538                        // Slow Motion Toggle (only show when animation is selected)
     539                        animationType && animationType !== '' && wp.element.createElement(
     540                            'div',
     541                            {
     542                                style: { marginBottom: '16px' },
     543                                className: 'serend-toggle-wrapper'
     544                            },
     545                            wp.element.createElement( ToggleControl, {
     546                                label: ( typeof serendAnimations !== 'undefined' && serendAnimations.i18n ) ? serendAnimations.i18n.slowLabel : 'Slow Motion',
     547                                checked: animationSlow,
     548                                onChange: setAnimationSlow,
     549                                help: ( typeof serendAnimations !== 'undefined' && serendAnimations.i18n ) ? serendAnimations.i18n.slowHelp : 'Makes the animation play slower (1.5x duration).'
     550                            } )
     551                        ),
     552                        // Delay Control (only show when animation is selected)
     553                        animationType && animationType !== '' && wp.element.createElement( RangeControl, {
     554                            className: 'serend-delay-control',
    161555                            label: ( typeof serendAnimations !== 'undefined' && serendAnimations.i18n ) ? serendAnimations.i18n.delayLabel : 'Delay (ms)',
    162556                            value: animationDelay,
     
    166560                            step: 100,
    167561                            help: ( typeof serendAnimations !== 'undefined' && serendAnimations.i18n ) ? serendAnimations.i18n.delayHelp : 'Delay in milliseconds before the animation starts. Useful for staggered animations.'
    168                         } )
     562                        } ),
     563                        // Preview Button (only show when animation is selected)
     564                        animationType && animationType !== '' && wp.element.createElement( Button, {
     565                            variant: 'secondary',
     566                            className: 'serend-preview-button',
     567                            onClick: triggerAnimationPreview,
     568                            disabled: isAnimating,
     569                            style: { width: '100%', marginTop: '12px' }
     570                        }, isAnimating ? __( 'Animating...', 'serend-animations' ) : __( 'Show Animation', 'serend-animations' ) )
    169571                    )
    170572                )
     
    174576
    175577    // Register Filters.
    176     addFilter(
    177         'blocks.registerBlockType',
    178         'serend-animations/add-animation-attribute',
    179         addAnimationAttribute
    180     );
    181 
     578    // Note: We don't add animation attributes to blocks anymore.
     579    // We work purely with className, which is supported by all blocks.
    182580    addFilter(
    183581        'editor.BlockEdit',
  • serend-animations/trunk/includes/serend-animations-functions.php

    r3368477 r3390988  
    7171function serend_animations_admin_page() {
    7272    // Handle form submission.
    73     if ( isset( $_POST['serend_animations_settings_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['serend_animations_settings_nonce'] ) ), 'serend_animations_settings' ) ) {
     73    if ( isset( $_POST['serend_animations_settings_nonce'] ) &&
     74         check_admin_referer( 'serend_animations_settings', 'serend_animations_settings_nonce' ) ) {
    7475        if ( isset( $_POST['debug_mode'] ) ) {
    7576            update_option( 'serend_animations_debug_mode', '1' );
     
    110111                        <li><?php echo esc_html__( 'Open any block in the Gutenberg editor', 'serend-animations' ); ?></li>
    111112                        <li><?php echo esc_html__( 'Look in the right sidebar under "Serend Animation"', 'serend-animations' ); ?></li>
    112                         <li><?php echo esc_html__( 'Choose an animation from the dropdown list', 'serend-animations' ); ?></li>
    113                         <li><strong><?php echo esc_html__( 'NEW:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Use the delay slider for staggered animations', 'serend-animations' ); ?></li>
     113                        <li><?php echo esc_html__( 'Choose an animation type from the dropdown (Fade, Zoom, Rotate, Bounce, Flip)', 'serend-animations' ); ?></li>
     114                        <li><strong><?php echo esc_html__( 'NEW:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Select animation direction with visual arrow buttons', 'serend-animations' ); ?></li>
     115                        <li><strong><?php echo esc_html__( 'NEW:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Enable Slow Motion toggle for 1.5x slower animations', 'serend-animations' ); ?></li>
     116                        <li><?php echo esc_html__( 'Use the delay slider for staggered animations', 'serend-animations' ); ?></li>
    114117                        <li><?php echo esc_html__( 'The animation class is automatically added to "Additional CSS class(es)"', 'serend-animations' ); ?></li>
    115118                        <li><?php echo esc_html__( 'Save the page and view the result on the frontend', 'serend-animations' ); ?></li>
     
    128131            <div class="serend-column">
    129132                <div class="serend-card">
    130                     <h2><?php echo esc_html__( 'Available Animations', 'serend-animations' ); ?></h2>
    131                     <ul>
    132                         <li><strong><?php echo esc_html__( 'Fade In (from bottom):', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element fades in from bottom to top', 'serend-animations' ); ?></li>
    133                         <li><strong><?php echo esc_html__( 'Fade In (from left):', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element fades in from the left', 'serend-animations' ); ?></li>
    134                         <li><strong><?php echo esc_html__( 'Fade In (from right):', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element fades in from the right', 'serend-animations' ); ?></li>
    135                         <li><strong><?php echo esc_html__( 'Fade In Slow:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Slower fade-in animation from bottom', 'serend-animations' ); ?></li>
    136                         <li><strong><?php echo esc_html__( 'Zoom In:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element zooms from small to normal size', 'serend-animations' ); ?></li>
    137                         <li><strong><?php echo esc_html__( 'Rotate In:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element rotates while fading in', 'serend-animations' ); ?></li>
    138                         <li><strong><?php echo esc_html__( 'Bounce In:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element "bounces" in from bottom', 'serend-animations' ); ?></li>
    139                         <li><strong><?php echo esc_html__( 'Flip In:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element flips in with 3D rotation', 'serend-animations' ); ?></li>
     133                    <h2><?php echo esc_html__( 'Animation Types', 'serend-animations' ); ?></h2>
     134                    <ul>
     135                        <li><strong><?php echo esc_html__( 'Fade:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element fades in smoothly', 'serend-animations' ); ?></li>
     136                        <li><strong><?php echo esc_html__( 'Zoom:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element zooms from small to normal size', 'serend-animations' ); ?></li>
     137                        <li><strong><?php echo esc_html__( 'Rotate:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element rotates while fading in', 'serend-animations' ); ?></li>
     138                        <li><strong><?php echo esc_html__( 'Bounce:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element bounces in playfully', 'serend-animations' ); ?></li>
     139                        <li><strong><?php echo esc_html__( 'Flip:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Element flips in with 3D rotation', 'serend-animations' ); ?></li>
     140                    </ul>
     141                    <p style="margin-top: 15px; padding: 10px; background: #e7f3ff; border-left: 4px solid #2271b1;">
     142                        <strong><?php echo esc_html__( 'Universal Direction Support:', 'serend-animations' ); ?></strong><br>
     143                        <?php echo esc_html__( 'All animation types support 4 directions (bottom, left, right, top) via visual arrow buttons.', 'serend-animations' ); ?>
     144                    </p>
     145                </div>
     146               
     147                <div class="serend-card" style="margin-top: 20px;">
     148                    <h2><?php echo esc_html__( 'Direction & Slow Motion', 'serend-animations' ); ?></h2>
     149                    <p><?php echo esc_html__( 'Enhanced animation controls:', 'serend-animations' ); ?></p>
     150                    <ul>
     151                        <li><strong><?php echo esc_html__( 'Direction Buttons:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Visual arrow buttons to select animation direction', 'serend-animations' ); ?></li>
     152                        <li><strong><?php echo esc_html__( 'Slow Motion:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Toggle to make any animation 1.5x slower', 'serend-animations' ); ?></li>
     153                        <li><strong><?php echo esc_html__( '20 Variants:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'Each animation type × 4 directions', 'serend-animations' ); ?></li>
    140154                    </ul>
    141155                </div>
     
    153167                    </ul>
    154168                    <p><strong><?php echo esc_html__( 'Tip:', 'serend-animations' ); ?></strong> <?php echo esc_html__( 'For beautiful staggered effects use 200-400ms intervals between blocks.', 'serend-animations' ); ?></p>
     169                </div>
     170               
     171                <div class="serend-card" style="margin-top: 20px;">
     172                    <h2><?php echo esc_html__( 'Example Combinations', 'serend-animations' ); ?></h2>
     173                    <ul>
     174                        <li><?php echo esc_html__( 'Fade + Left direction + Slow Motion', 'serend-animations' ); ?></li>
     175                        <li><?php echo esc_html__( 'Zoom + Top direction + 200ms delay', 'serend-animations' ); ?></li>
     176                        <li><?php echo esc_html__( 'Bounce + Bottom direction (default)', 'serend-animations' ); ?></li>
     177                        <li><?php echo esc_html__( 'Rotate + Right direction + Slow Motion', 'serend-animations' ); ?></li>
     178                        <li><?php echo esc_html__( 'Flip + Any direction + 400ms delay', 'serend-animations' ); ?></li>
     179                    </ul>
     180                    <p style="margin-top: 10px; font-size: 12px; color: #666;">
     181                        <?php echo esc_html__( 'Mix and match direction, slow motion, and delay for endless creative possibilities!', 'serend-animations' ); ?>
     182                    </p>
    155183                </div>
    156184            </div>
  • serend-animations/trunk/languages/serend-animations-de_DE.po

    r3368477 r3390988  
    7070msgstr "Schaue im rechten Sidebar unter \"Serend Animation\""
    7171
    72 msgid "Choose an animation from the dropdown list"
    73 msgstr "Wähle eine Animation aus der Dropdown-Liste"
     72msgid "Choose an animation type from the dropdown (Fade, Zoom, Rotate, Bounce, Flip)"
     73msgstr "Wähle einen Animationstyp aus der Dropdown-Liste (Fade, Zoom, Rotate, Bounce, Flip)"
    7474
    7575msgid "NEW:"
    7676msgstr "NEU:"
    7777
     78msgid "Select animation direction with visual arrow buttons"
     79msgstr "Wähle die Animationsrichtung mit visuellen Pfeil-Buttons"
     80
     81msgid "Enable Slow Motion toggle for 1.5x slower animations"
     82msgstr "Aktiviere Slow Motion für 1,5x langsamere Animationen"
     83
    7884msgid "Use the delay slider for staggered animations"
    7985msgstr "Nutze den Verzögerungs-Slider für gestaffelte Animationen"
     
    8894msgstr "Screenshot der die Verwendung von Serend Animations zeigt"
    8995
    90 # Admin page - Available Animations section
    91 msgid "Available Animations"
    92 msgstr "Verfügbare Animationen"
    93 
    94 msgid "Fade In (from bottom):"
    95 msgstr "Fade In (von unten):"
    96 
    97 msgid "Element fades in from bottom to top"
    98 msgstr "Element faded von unten nach oben ein"
    99 
    100 msgid "Fade In (from left):"
    101 msgstr "Fade In (von links):"
    102 
    103 msgid "Element fades in from the left"
    104 msgstr "Element faded von links ein"
    105 
    106 msgid "Fade In (from right):"
    107 msgstr "Fade In (von rechts):"
    108 
    109 msgid "Element fades in from the right"
    110 msgstr "Element faded von rechts ein"
    111 
    112 msgid "Fade In Slow:"
    113 msgstr "Fade In Langsam:"
    114 
    115 msgid "Slower fade-in animation from bottom"
    116 msgstr "Langsamere Fade-In Animation von unten"
    117 
    118 msgid "Zoom In:"
    119 msgstr "Zoom In:"
     96# Admin page - Animation Types section
     97msgid "Animation Types"
     98msgstr "Animationstypen"
     99
     100msgid "Fade:"
     101msgstr "Fade:"
     102
     103msgid "Element fades in smoothly"
     104msgstr "Element wird sanft eingeblendet"
     105
     106msgid "Zoom:"
     107msgstr "Zoom:"
    120108
    121109msgid "Element zooms from small to normal size"
    122110msgstr "Element zoomt von klein zu normal"
    123111
    124 msgid "Rotate In:"
    125 msgstr "Rotate In:"
     112msgid "Rotate:"
     113msgstr "Rotate:"
    126114
    127115msgid "Element rotates while fading in"
    128116msgstr "Element rotiert während es eingeblendet wird"
    129117
    130 msgid "Bounce In:"
    131 msgstr "Bounce In:"
    132 
    133 msgid "Element \"bounces\" in from bottom"
    134 msgstr "Element \"hüpft\" von unten herein"
    135 
    136 msgid "Flip In:"
    137 msgstr "Flip In:"
     118msgid "Bounce:"
     119msgstr "Bounce:"
     120
     121msgid "Element bounces in playfully"
     122msgstr "Element hüpft spielerisch herein"
     123
     124msgid "Flip:"
     125msgstr "Flip:"
    138126
    139127msgid "Element flips in with 3D rotation"
    140128msgstr "Element dreht sich in 3D herein"
     129
     130msgid "Universal Direction Support:"
     131msgstr "Universelle Richtungsunterstützung:"
     132
     133msgid "All animation types support 4 directions (bottom, left, right, top) via visual arrow buttons."
     134msgstr "Alle Animationstypen unterstützen 4 Richtungen (unten, links, rechts, oben) über visuelle Pfeil-Buttons."
     135
     136# Admin page - Direction & Slow Motion section
     137msgid "Direction & Slow Motion"
     138msgstr "Richtung & Slow Motion"
     139
     140msgid "Enhanced animation controls:"
     141msgstr "Erweiterte Animations-Steuerungen:"
     142
     143msgid "Direction Buttons:"
     144msgstr "Richtungs-Buttons:"
     145
     146msgid "Visual arrow buttons to select animation direction"
     147msgstr "Visuelle Pfeil-Buttons zur Auswahl der Animationsrichtung"
     148
     149msgid "Slow Motion:"
     150msgstr "Slow Motion:"
     151
     152msgid "Toggle to make any animation 1.5x slower"
     153msgstr "Toggle um jede Animation 1,5x langsamer zu machen"
     154
     155msgid "20 Variants:"
     156msgstr "20 Varianten:"
     157
     158msgid "Each animation type × 4 directions"
     159msgstr "Jeder Animationstyp × 4 Richtungen"
     160
     161# Admin page - Example Combinations section
     162msgid "Example Combinations"
     163msgstr "Beispiel-Kombinationen"
     164
     165msgid "Fade + Left direction + Slow Motion"
     166msgstr "Fade + Linke Richtung + Slow Motion"
     167
     168msgid "Zoom + Top direction + 200ms delay"
     169msgstr "Zoom + Obere Richtung + 200ms Verzögerung"
     170
     171msgid "Bounce + Bottom direction (default)"
     172msgstr "Bounce + Untere Richtung (Standard)"
     173
     174msgid "Rotate + Right direction + Slow Motion"
     175msgstr "Rotate + Rechte Richtung + Slow Motion"
     176
     177msgid "Flip + Any direction + 400ms delay"
     178msgstr "Flip + Beliebige Richtung + 400ms Verzögerung"
     179
     180msgid "Mix and match direction, slow motion, and delay for endless creative possibilities!"
     181msgstr "Kombiniere Richtung, Slow Motion und Verzögerung für endlose kreative Möglichkeiten!"
    141182
    142183# Admin page - Staggered Animations section
  • serend-animations/trunk/readme.txt

    r3368477 r3390988  
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 1.0.1
     7Stable tag: 1.0.3
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1313== Description ==
    1414
    15 Serend Animations makes it incredibly easy to add stunning scroll animations to any Gutenberg block. Simply select a block, choose an animation from the dropdown, and watch your content come to life as visitors scroll through your page.
     15Serend Animations makes it incredibly easy to add stunning scroll animations to any Gutenberg block. Choose an animation type, select a direction with intuitive arrow buttons, optionally enable slow motion, and watch your content come to life as visitors scroll through your page.
     16
     17**NEW in v1.0.3:** Visual direction picker with arrow buttons! All animation types now support all 4 directions (top, right, bottom, left), plus a universal Slow Motion toggle for dramatic effects.
    1618
    1719Why choose Serend Animations?
    1820
     21- **Visual Direction Control**: Intuitive arrow buttons to select animation direction - no more guessing!
     22- **20 Animation Variants**: 5 animation types × 4 directions = endless creative possibilities
     23- **Slow Motion Toggle**: Make any animation 1.5x slower for more dramatic effects
    1924- Ultra Lightweight: Pure CSS animations with minimal JavaScript - only 8KB total
    2025- GDPR Friendly: No tracking, no cookies, no external connections - 100% privacy compliant 
     
    2732= Features =
    2833
    29 - 8 Beautiful Animation Types: Fade In (from bottom, left, right), Fade In Slow, Zoom In, Rotate In, Bounce In, and Flip In
     34- 5 Animation Types with 4 Directions Each: 20 beautiful animation variants to choose from
     35- Visual Direction Picker: Intuitive arrow buttons to select animation direction (top, right, bottom, left)
     36- Slow Motion Toggle: Make any animation 1.5x slower for more dramatic effects
    3037- Staggered Animations: Use the delay slider to create cascading animation effects
    31 - One-Click Integration: No code required - just select an animation from the dropdown
     38- One-Click Integration: No code required - just select an animation and direction
    3239- Performance Optimized: Uses native Intersection Observer API for smooth performance
    3340- Works with All Blocks: Compatible with every Gutenberg block
     
    38451. Open any block in the Gutenberg editor
    39462. Look for "Serend Animation" in the block inspector panel
    40 3. Choose an animation from the dropdown
    41 4. Optionally set a delay for staggered effects
    42 5. Save and view your animated content on the frontend
     473. Choose an animation type (Fade, Zoom, Rotate, Bounce, or Flip)
     484. Select a direction using the visual arrow buttons
     495. Optionally enable "Slow Motion" for a more dramatic effect
     506. Optionally set a delay for staggered effects
     517. Click "Show Animation" to preview in the editor
     528. Save and view your animated content on the frontend
    4353
    4454= Animation Types =
    4555
    46 - Fade In (from bottom): Elements gracefully fade in from below
    47 - Fade In (from left): Elements slide in from the left side
    48 - Fade In (from right): Elements slide in from the right side
    49 - Fade In Slow: A slower, more elegant fade from below
    50 - Zoom In: Elements scale up from smaller size
    51 - Rotate In: Elements rotate while fading in
    52 - Bounce In: Elements bounce in with elastic effect
    53 - Flip In: Elements flip in with 3D rotation effect
     56Each animation type supports 4 directions (top, right, bottom, left):
     57
     58- **Fade**: Elements gracefully fade in while sliding from the selected direction
     59- **Zoom**: Elements scale up from smaller size while moving from the selected direction
     60- **Rotate**: Elements rotate while scaling and moving from the selected direction
     61- **Bounce**: Elements bounce in with elastic effect from the selected direction
     62- **Flip**: Elements flip in with 3D rotation (X-axis for top/bottom, Y-axis for left/right)
     63
     64Plus, every animation can be made slower with the "Slow Motion" toggle for more dramatic effects!
    5465
    5566= Perfect For =
     
    8495== Frequently Asked Questions ==
    8596
     97= How do the direction buttons work? =
     98
     99Instead of selecting from a long dropdown list, you now get 4 intuitive arrow buttons that visually show the direction your animation will come from. Click the up arrow for animations from the top, right arrow for right, etc. It's much more intuitive and visual!
     100
     101= Can I make animations slower? =
     102
     103Yes! The new "Slow Motion" toggle makes any animation 1.5x slower. This works for all animation types (Fade, Zoom, Rotate, Bounce, Flip) and creates more dramatic, elegant effects.
     104
    86105= Does this work with all Gutenberg blocks? =
    87106
     
    98117= Can I use multiple animations on the same page? =
    99118
    100 Absolutely! You can use different animations on different blocks, and even create staggered effects using the delay slider.
     119Absolutely! You can use different animations on different blocks, mix directions, combine slow motion effects, and even create staggered effects using the delay slider. With 20 animation variants, the creative possibilities are endless!
    101120
    102121= Does this work on mobile devices? =
     
    112131Yes, 100%! The plugin is fully GDPR compliant as it doesn't collect any user data, doesn't use cookies, doesn't make external connections, and doesn't track users in any way. It's pure CSS and JavaScript that runs entirely on your website.
    113132
     133= Can I preview animations in the editor? =
     134
     135Yes! Click the "Show Animation" button in the block inspector to see a live preview of your animation in the Gutenberg editor. The preview automatically plays when you change the animation type, direction, or enable slow motion.
     136
    114137= Can I customize the animations? =
    115138
    116 Absolutely! The plugin provides 8 carefully crafted animations out of the box, but you can easily extend or customize them with your own CSS. The code is clean and well-documented for easy customization.
     139Absolutely! The plugin provides 20 carefully crafted animation variants (5 types × 4 directions) out of the box, plus the slow motion modifier. You can also extend or customize them with your own CSS. The code is clean and well-documented for easy customization.
    117140
    118141= How do I enable debug mode? =
     
    132155
    133156== Changelog ==
     157
     158= 1.0.3 =
     159* **NEW:** Visual direction picker with arrow buttons for all animations
     160* **NEW:** Slow Motion toggle - make any animation 1.5x slower
     161* **NEW:** All animation types now support all 4 directions (top, right, bottom, left)
     162* **NEW:** 20 animation variants total (5 types × 4 directions)
     163* Simplified UI - cleaner animation type dropdown
     164* Direction selection moved from dropdown to intuitive button grid
     165* Delay changes no longer trigger editor preview (frontend-only)
     166* Fixed scroll-fade-in-top and other directional variants not animating
     167* Frontend JavaScript now uses future-proof attribute selectors
     168* Better CSS organization with clear section comments
     169* Universal animation-slow modifier class
     170* Enhanced editor preview with all directional keyframes
     171* Improved class management for slow modifier and directions
     172
     173= 1.0.2 =
     174* Added live animation preview in Gutenberg editor with "Show Animation" button
     175* Automatic animation preview when selecting or changing animations
     176* Automatic preview when changing delay values
     177* Full support for all third-party blocks (Yoast SEO, Rank Math, WooCommerce, etc.)
     178* Fixed animation class cleanup - no more orphaned fragments
     179* Fixed correct animation detection for compound classes
     180* Elements now remain visible in editor while supporting previews
     181* Animation dropdown now shows correct selection
     182* All Fade In variants now work correctly
     183* Improved className management with better filtering
     184* Production-ready code (removed debug logs)
     185* Better iframe handling for modern WordPress editor
    134186
    135187= 1.0.1 =
     
    157209== Upgrade Notice ==
    158210
     211= 1.0.3 =
     212Major feature update! Visual direction picker with arrow buttons for ALL animations, universal Slow Motion toggle, and 20 animation variants (5 types × 4 directions). Much more flexible and intuitive animation control. Highly recommended update!
     213
     214= 1.0.2 =
     215Major editor improvements! Live animation preview, automatic previews when changing animations, full third-party block support, and better animation class management. Highly recommended update.
     216
    159217= 1.0.1 =
    160218Code quality improvements and WordPress standards compliance. No new features, but better maintainability and performance.
  • serend-animations/trunk/serend-animations.php

    r3368477 r3390988  
    44 * Plugin URI: https://wordpress.org/plugins/serend-animations/
    55 * Description: Add beautiful scroll animations to Gutenberg blocks with a simple dropdown. Choose from fade, zoom, rotate, bounce and flip effects.
    6  * Version: 1.0.1
     6 * Version: 1.0.3
    77 * Author: serend.design
    88 * Author URI: https://serend.design
     
    1212 * Domain Path: /languages
    1313 * Requires at least: 6.0
    14  * Tested up to: 6.6
     14 * Tested up to: 6.8
    1515 * Requires PHP: 7.4
    1616 *
     
    4444
    4545    /**
     46     * Plugin version.
     47     *
     48     * @var string
     49     */
     50    const VERSION = '1.0.3';
     51
     52    /**
    4653     * Constructor.
    4754     */
    4855    public function __construct() {
     56        add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
    4957        add_action( 'init', array( $this, 'init' ) );
    5058    }
    5159
    5260    /**
     61     * Load plugin text domain for translations.
     62     */
     63    public function load_textdomain() {
     64        load_plugin_textdomain(
     65            'serend-animations',
     66            false,
     67            dirname( plugin_basename( __FILE__ ) ) . '/languages'
     68        );
     69    }
     70
     71    /**
    5372     * Initialize plugin.
    5473     */
    5574    public function init() {
     75        // Register Block Assets (loads in both editor and frontend).
     76        add_action( 'enqueue_block_assets', array( $this, 'enqueue_block_assets' ) );
     77
    5678        // Register Block Editor Assets.
    5779        add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
     
    6284
    6385    /**
     86     * Load Block Assets (both editor and frontend).
     87     * This ensures CSS is loaded in the iframe editor.
     88     */
     89    public function enqueue_block_assets() {
     90        // Only in admin/editor context.
     91        if ( is_admin() ) {
     92        wp_enqueue_style(
     93            'serend-animations-editor-css',
     94            plugin_dir_url( __FILE__ ) . 'includes/assets/css/animations.css',
     95            array(),
     96            self::VERSION
     97        );
     98        }
     99    }
     100
     101    /**
    64102     * Load Block Editor Assets.
    65103     */
    66104    public function enqueue_block_editor_assets() {
     105        // Enqueue editor-specific CSS for custom styling
     106        wp_enqueue_style(
     107            'serend-animations-editor-styles',
     108            plugin_dir_url( __FILE__ ) . 'includes/assets/css/editor.css',
     109            array(),
     110            self::VERSION
     111        );
     112
    67113        wp_enqueue_script(
    68114            'serend-animations-editor',
    69115            plugin_dir_url( __FILE__ ) . 'includes/assets/js/editor.js',
    70116            array( 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-compose', 'wp-data', 'wp-hooks', 'wp-i18n' ),
    71             '1.0.0',
     117            self::VERSION,
    72118            true
    73119        );
    74120
    75121        // Set up script translations for JavaScript.
    76         wp_set_script_translations( 'serend-animations-editor', 'serend-animations', __DIR__ . '/languages' );
     122        wp_set_script_translations( 'serend-animations-editor', 'serend-animations', plugin_dir_path( __FILE__ ) . 'languages' );
    77123
    78124        // Localize strings for the editor.
     
    81127            'serendAnimations',
    82128            array(
    83                 'animations' => array(
    84                     ''                     => __( 'No Animation', 'serend-animations' ),
    85                     'scroll-fade-in'       => __( 'Fade In (from bottom)', 'serend-animations' ),
    86                     'scroll-fade-in-left'  => __( 'Fade In (from left)', 'serend-animations' ),
    87                     'scroll-fade-in-right' => __( 'Fade In (from right)', 'serend-animations' ),
    88                     'scroll-fade-in-slow'  => __( 'Fade In Slow (from bottom)', 'serend-animations' ),
    89                     'scroll-zoom-in'       => __( 'Zoom In', 'serend-animations' ),
    90                     'scroll-rotate-in'     => __( 'Rotate In', 'serend-animations' ),
    91                     'scroll-bounce-in'     => __( 'Bounce In', 'serend-animations' ),
    92                     'scroll-flip-in'       => __( 'Flip In', 'serend-animations' ),
     129                'animationTypes' => array(
     130                    ''         => __( 'No Animation', 'serend-animations' ),
     131                    'fade'     => __( 'Fade', 'serend-animations' ),
     132                    'zoom'     => __( 'Zoom', 'serend-animations' ),
     133                    'rotate'   => __( 'Rotate', 'serend-animations' ),
     134                    'bounce'   => __( 'Bounce', 'serend-animations' ),
     135                    'flip'     => __( 'Flip', 'serend-animations' ),
    93136                ),
    94137                'i18n'       => array(
    95138                    'panelTitle'      => __( 'Serend Animation', 'serend-animations' ),
    96                     'scrollAnimation' => __( 'Scroll Animation', 'serend-animations' ),
     139                    'animationType'   => __( 'Animation Type', 'serend-animations' ),
    97140                    'animationHelp'   => __( 'Choose an animation that triggers when scrolling.', 'serend-animations' ),
     141                    'directionLabel'  => __( 'Direction', 'serend-animations' ),
     142                    'directionHelp'   => __( 'Select the direction from which the animation starts.', 'serend-animations' ),
     143                    'slowLabel'       => __( 'Slow Motion', 'serend-animations' ),
     144                    'slowHelp'        => __( 'Makes the animation play slower (1.5x duration).', 'serend-animations' ),
    98145                    'delayLabel'      => __( 'Delay (ms)', 'serend-animations' ),
    99146                    'delayHelp'       => __( 'Delay in milliseconds before the animation starts. Useful for staggered animations.', 'serend-animations' ),
     
    111158            plugin_dir_url( __FILE__ ) . 'includes/assets/css/animations.css',
    112159            array(),
    113             '1.0.0'
     160            self::VERSION
    114161        );
    115162
     
    118165            plugin_dir_url( __FILE__ ) . 'includes/assets/js/animations.js',
    119166            array(),
    120             '1.0.0',
     167            self::VERSION,
    121168            true
    122169        );
     
    145192            'scroll-fade-in-left',
    146193            'scroll-fade-in-right',
    147             'scroll-fade-in-slow',
     194            'scroll-fade-in-top',
    148195            'scroll-zoom-in',
     196            'scroll-zoom-in-left',
     197            'scroll-zoom-in-right',
     198            'scroll-zoom-in-top',
    149199            'scroll-rotate-in',
     200            'scroll-rotate-in-left',
     201            'scroll-rotate-in-right',
     202            'scroll-rotate-in-top',
    150203            'scroll-bounce-in',
     204            'scroll-bounce-in-left',
     205            'scroll-bounce-in-right',
     206            'scroll-bounce-in-top',
    151207            'scroll-flip-in',
    152         );
    153 
    154         // Build the debug script as a string.
     208            'scroll-flip-in-left',
     209            'scroll-flip-in-right',
     210            'scroll-flip-in-top',
     211            'animation-slow',
     212        );
     213
     214        // Build the debug script as a string using safer DOM methods.
    155215        $debug_script = 'document.addEventListener("DOMContentLoaded", function() {
    156216            // Create debug overlay
     
    158218            debugDiv.style.cssText = "position: fixed; top: 10px; right: 10px; background: #fff; border: 1px solid #ccc; padding: 10px; z-index: 9999; font-family: monospace; font-size: 12px; max-width: 300px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);";
    159219           
    160             var content = "<h4 style=\"margin: 0 0 8px 0;\">' . esc_js( __( 'Animation Classes Debug', 'serend-animations' ) ) . '</h4>";
    161             content += "<p style=\"margin: 0 0 8px 0; font-size: 11px;\">' . esc_js( __( 'Live count from rendered HTML:', 'serend-animations' ) ) . '</p>";
     220            // Create header
     221            var header = document.createElement("h4");
     222            header.style.cssText = "margin: 0 0 8px 0;";
     223            header.textContent = ' . wp_json_encode( __( 'Animation Classes Debug', 'serend-animations' ) ) . ';
     224            debugDiv.appendChild(header);
     225           
     226            // Create description
     227            var desc = document.createElement("p");
     228            desc.style.cssText = "margin: 0 0 8px 0; font-size: 11px;";
     229            desc.textContent = ' . wp_json_encode( __( 'Live count from rendered HTML:', 'serend-animations' ) ) . ';
     230            debugDiv.appendChild(desc);
    162231            ';
    163232
     
    165234            $safe_var_name = str_replace( '-', '_', $class );
    166235            $debug_script .= 'var elements_' . esc_js( $safe_var_name ) . ' = document.querySelectorAll(".' . esc_js( $class ) . '");
    167             content += "<div>' . esc_js( $class ) . ': " + elements_' . esc_js( $safe_var_name ) . '.length + " ' . esc_js( __( 'times used', 'serend-animations' ) ) . '</div>";
     236            var row_' . esc_js( $safe_var_name ) . ' = document.createElement("div");
     237            row_' . esc_js( $safe_var_name ) . '.textContent = "' . esc_js( $class ) . ': " + elements_' . esc_js( $safe_var_name ) . '.length + " ' . esc_js( __( 'times used', 'serend-animations' ) ) . '";
     238            debugDiv.appendChild(row_' . esc_js( $safe_var_name ) . ');
    168239            ';
    169240        }
    170241
    171         $debug_script .= 'debugDiv.innerHTML = content;
    172             document.body.appendChild(debugDiv);
     242        $debug_script .= 'document.body.appendChild(debugDiv);
    173243        });';
    174244
Note: See TracChangeset for help on using the changeset viewer.