Преглед изворни кода

Merge pull request #498 from SimoTod/feature/css-animations

Feature - CSS animations
Caleb Porzio пре 5 година
родитељ
комит
b6f2b24630
5 измењених фајлова са 144 додато и 0 уклоњено
  1. 5 0
      dist/alpine-ie11.js
  2. 5 0
      dist/alpine.js
  3. 46 0
      examples/index.html
  4. 4 0
      src/utils.js
  5. 84 0
      test/transition.spec.js

+ 5 - 0
dist/alpine-ie11.js

@@ -5770,6 +5770,11 @@
       // Note: Safari's transitionDuration property will list out comma separated transition durations
       // for every single transition property. Let's grab the first one and call it a day.
       var duration = Number(getComputedStyle(el).transitionDuration.replace(/,.*/, '').replace('s', '')) * 1000;
+
+      if (duration === 0) {
+        duration = Number(getComputedStyle(el).animationDuration.replace('s', '')) * 1000;
+      }
+
       stages.show();
       requestAnimationFrame(function () {
         var _this14 = this;

+ 5 - 0
dist/alpine.js

@@ -369,6 +369,11 @@
       // Note: Safari's transitionDuration property will list out comma separated transition durations
       // for every single transition property. Let's grab the first one and call it a day.
       let duration = Number(getComputedStyle(el).transitionDuration.replace(/,.*/, '').replace('s', '')) * 1000;
+
+      if (duration === 0) {
+        duration = Number(getComputedStyle(el).animationDuration.replace('s', '')) * 1000;
+      }
+
       stages.show();
       requestAnimationFrame(() => {
         stages.end();

+ 46 - 0
examples/index.html

@@ -12,6 +12,27 @@
             .scale-100 { transform: scale(1); }
             .ease-in { transition-timing-function: cubic-bezier(0.4, 0, 1, 1); }
             .ease-out { transition-timing-function: cubic-bezier(0, 0, 0.2, 1); }
+
+            /** Thanks Animate.css */
+            .animation {animation-duration: 1000ms; --animate-duration: 1000ms; }
+            @keyframes jackInTheBox {
+                from { opacity: 0; transform: scale(0.1) rotate(30deg); transform-origin: center bottom; }
+                50% { transform: rotate(-10deg); }
+                70% { transform: rotate(3deg); }
+                to { opacity: 1; transform: scale(1); }
+            }
+            @keyframes hinge {
+                0% {animation-timing-function: ease-in-out;}
+                20%, 60% { transform: rotate3d(0, 0, 1, 80deg); animation-timing-function: ease-in-out; }
+                40%, 80% { transform: rotate3d(0, 0, 1, 60deg); animation-timing-function: ease-in-out; opacity: 1; }
+                to { transform: translate3d(0, 700px, 0); opacity: 0; }
+            }
+            .jackInTheBox {animation-name: jackInTheBox;}
+            .hinge {
+                animation-duration: calc(var(--animate-duration) * 2);
+                animation-name: hinge;
+                transform-origin: top left;
+            }
         </style>
 
         <script src="/dist/alpine.js" defer></script>
@@ -302,6 +323,31 @@
                     </td>
                 </tr>
 
+                <tr>
+                    <td>CSS animations</td>
+                    <td>
+                        <div x-data="{ open: false }">
+                            <button x-on:click="open= ! open">
+                                Open Modal
+                            </button>
+
+                            <div x-show="open"
+                                x-transition:enter="animation jackInTheBox"
+                                x-transition:leave="animation hinge">
+                                    <div>
+                                        hey
+                                    </div>
+                                    <div>
+                                        <button x-on:click="open= false" type="button">
+                                            Cancel
+                                        </button>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </td>
+                </tr>
+
                 <tr>
                     <td>Transitions (with x-if)</td>
                     <td>

+ 4 - 0
src/utils.js

@@ -360,6 +360,10 @@ export function transition(el, stages) {
         // for every single transition property. Let's grab the first one and call it a day.
         let duration = Number(getComputedStyle(el).transitionDuration.replace(/,.*/, '').replace('s', '')) * 1000
 
+        if (duration === 0) {
+            duration = Number(getComputedStyle(el).animationDuration.replace('s', '')) * 1000
+        }
+
         stages.show()
 
         requestAnimationFrame(() => {

+ 84 - 0
test/transition.spec.js

@@ -536,3 +536,87 @@ async function assertTransitionHelperStyleAttributeValues(xShowDirective, styleA
 
     expect(document.querySelector('span').getAttribute('style')).toEqual(styleAttributeExpectations[++index])
 }
+
+test('x-transition supports css animation', async () => {
+    jest.spyOn(window, 'requestAnimationFrame').mockImplementation((callback) => {
+        setTimeout(callback, 0)
+    });
+
+    // (hardcoding 10ms animation time for later assertions)
+    jest.spyOn(window, 'getComputedStyle').mockImplementation(el => {
+        return {
+            transitionDuration: '0s',
+            animationDuration: '.1s'
+        }
+    });
+
+    document.body.innerHTML = `
+        <div x-data="{ show: false }">
+            <button x-on:click="show = ! show"></button>
+
+            <span
+                x-show="show"
+                x-transition:enter="animation-enter"
+                x-transition:leave="animation-leave"
+            ></span>
+        </div>
+    `
+
+    Alpine.start()
+
+    await wait(() => { expect(document.querySelector('span').getAttribute('style')).toEqual('display: none;') })
+
+    // Testing animation enter
+    document.querySelector('button').click()
+
+    // Wait for the first requestAnimationFrame
+    await new Promise((resolve) =>
+        setTimeout(() => {
+            resolve();
+        }, 0)
+    )
+    expect(document.querySelector('span').classList.contains('animation-enter')).toEqual(true)
+
+    // The class should still be there since the animationDuration property is 100ms
+    await new Promise((resolve) =>
+        setTimeout(() => {
+            resolve();
+        }, 99)
+    )
+    expect(document.querySelector('span').classList.contains('animation-enter')).toEqual(true)
+
+    // The class shouldn't be there anymore
+    await new Promise((resolve) =>
+        setTimeout(() => {
+            resolve();
+        }, 10)
+    )
+    expect(document.querySelector('span').classList.contains('animation-enter')).toEqual(false)
+
+    // Testing animation enter
+    document.querySelector('button').click()
+
+    // Wait for the first requestAnimationFrame
+    await new Promise((resolve) =>
+        setTimeout(() => {
+            resolve();
+        }, 0)
+    )
+    expect(document.querySelector('span').classList.contains('animation-leave')).toEqual(true)
+
+    // The class should still be there since the animationDuration property is 100ms
+    await new Promise((resolve) =>
+        setTimeout(() => {
+            resolve();
+        }, 99)
+    )
+    expect(document.querySelector('span').classList.contains('animation-leave')).toEqual(true)
+
+    // The class shouldn't be there anymore
+    await new Promise((resolve) =>
+        setTimeout(() => {
+            resolve();
+        }, 10)
+    )
+    expect(document.querySelector('span').classList.contains('animation-leave')).toEqual(false)
+})