Yansen 2 年之前
父節點
當前提交
e91ab64313

二進制
public/images/pg1/stele.png 查看文件


二進制
public/images/pg1/stele2.png 查看文件


二進制
public/images/pg1/stele3.png 查看文件


二進制
public/images/pg2/footer.png 查看文件


+ 28
- 0
src/components/Animate.vue 查看文件

@@ -0,0 +1,28 @@
1
+<template>
2
+  <component
3
+    :is="comp || 'div'"
4
+    class="animate__animated"
5
+    :class="{ [name]: ready }"
6
+    :style="style"
7
+  >
8
+    <slot />
9
+  </component>
10
+</template>
11
+
12
+<script setup>
13
+  import { computed } from 'vue';
14
+
15
+  const props = defineProps({
16
+    comp: undefined,
17
+    name: String,
18
+    delay: String,
19
+    ready: Boolean
20
+  })
21
+
22
+  const style = computed(() => {
23
+    return {
24
+      animationDelay: props.delay || 0,
25
+      visibility: props.ready ? 'visible' : 'hidden',
26
+    }
27
+  });
28
+</script>

+ 33
- 8
src/components/Bell.vue 查看文件

@@ -1,16 +1,26 @@
1 1
 <template>
2
-  <div class="bell-box">
2
+  <div class="bell-box" @click="toggle">
3 3
     <div>
4
-      <div class="center circle abs"><div class="inner" /></div>
5
-      <div class="center circle abs"><div class="outer" /></div>
4
+      <div class="center circle abs">
5
+        <div class="inner" :class="{'circle-ani': animate}" />
6
+      </div>
7
+      <div class="center circle abs">
8
+        <div class="outer" :class="{'circle-ani': animate}" />
9
+      </div>
6 10
       <div class="center bell-img">
7
-        <img src="/images/pg1/bell.png" alt="">
11
+        <img :class="{'bell-ani': animate}" src="/images/pg1/bell.png" alt="">
8 12
       </div>
9 13
     </div>
10 14
   </div>
11 15
 </template>
12 16
 
13 17
 <script setup>
18
+import { computed, ref } from 'vue';
19
+import { useModel } from '@zjxpcyc/vue-tiny-store';
20
+
21
+const { toggle, state } = useModel('audio');
22
+const animate = computed(() => state.value === 1);
23
+
14 24
 </script>
15 25
 
16 26
 <style lang="less" scoped>
@@ -48,12 +58,20 @@
48 58
       opacity: 0;
49 59
     }
50 60
 
61
+    .circle-ani {
62
+      animation-name: bell-circle-show;
63
+    }
64
+
51 65
     .inner {
52
-      animation: bell-circle-show 3s linear infinite;
66
+      animation-duration: 3s;
67
+      animation-timing-function: linear;
68
+      animation-iteration-count: infinite;
53 69
     }
54 70
 
55
-    .outer {      
56
-      animation: bell-circle-show 3s linear 1.5s infinite;
71
+    .outer {
72
+      animation-duration: 1.5s;
73
+      animation-timing-function: linear;
74
+      animation-iteration-count: infinite;
57 75
     }
58 76
 
59 77
     @keyframes bell-circle-show {
@@ -77,12 +95,19 @@
77 95
     position: relative;
78 96
     z-index: 2;
79 97
 
98
+    .bell-ani {
99
+      animation-name: bell-shake;
100
+    }
101
+
80 102
     img {
81 103
       flex: none;
82 104
       width: 80%;
83 105
 
84 106
       transform-origin: top center;
85
-      animation: bell-shake 1.4s ease-in-out infinite alternate;
107
+      animation-duration: 1.4s;
108
+      animation-timing-function: ease-in-out;
109
+      animation-iteration-count: infinite;
110
+      animation-direction: alternate;
86 111
     }
87 112
   }
88 113
 

+ 10
- 1
src/pages/index.vue 查看文件

@@ -1,5 +1,6 @@
1 1
 <template>
2
-  <div style="height: 100vh">
2
+  <div style="height: 100vh;">
3
+    <Bell class="bell abs" />
3 4
     <div class="pages slide-container">
4 5
       <div class="slide-page-2">
5 6
         <Pg1 class="container"/>
@@ -17,6 +18,7 @@
17 18
 <script setup>
18 19
 import { onMounted, ref, watch } from 'vue';
19 20
 import { useModel } from '@zjxpcyc/vue-tiny-store';
21
+import Bell from '@/components/Bell.vue';
20 22
 import Pg1 from './pg1/index.vue';
21 23
 import Pg2 from './pg2/index.vue';
22 24
 import Pg3 from './pg3/index.vue';
@@ -46,6 +48,13 @@ onMounted(() => {
46 48
 </script>
47 49
 
48 50
 <style lang="less">
51
+
52
+.bell {
53
+  right: 30px;
54
+  top: 30px;
55
+  z-index: 100;
56
+}
57
+
49 58
 .pages {
50 59
   position: relative;
51 60
   overflow-y: auto;

+ 14
- 7
src/pages/pg1/index.vue 查看文件

@@ -3,19 +3,21 @@
3 3
     <div class="conent">
4 4
       <div class="header">        
5 5
         <Logo class="logo" />
6
-        <Bell class="bell" />
7 6
       </div>
8
-      <div class="body">
7
+      <div class="body" parallax-offset='-100'>
9 8
         <Part1 class="part1" @clickImg="onClickImg" />
10 9
         <Part2 class="part2" />
11 10
         <Part3 class="part3" />
12 11
       </div>
13 12
     </div>
14 13
     <div class="footer abs animate__animated animate__fadeInUp" style="animation-delay: 4s">
15
-      <img src="/images/pg1/stele.png" alt="" parallax-offset='-100'>
14
+      <img src="/images/pg1/stele.png" alt="" parallax-offset='-200'>
15
+    </div>
16
+    <div class="footer stele2 animate__animated animate__fadeInUp" style="animation-delay: 4s">
17
+      <img src="/images/pg1/stele2.png" alt="" parallax-offset='-230'>
16 18
     </div>
17 19
     <div class="footer abs animate__animated animate__fadeInUp" style="animation-delay: 4.5s">
18
-      <img src="/images/pg1/wall.png" alt="" parallax-offset='-100'>
20
+      <img src="/images/pg1/wall.png" alt="" parallax-offset='-250'>
19 21
     </div>
20 22
   </div>
21 23
 </template>
@@ -35,9 +37,6 @@
35 37
     router.push('/bk1');
36 38
   }
37 39
 
38
-  onMounted(() => {
39
-  })
40
-
41 40
 </script>
42 41
 
43 42
 <style lang="less" scoped>
@@ -75,4 +74,12 @@
75 74
     width: 100vw;
76 75
     z-index: 10;
77 76
   }
77
+
78
+  .stele2 {
79
+    position: absolute;
80
+    left: auto;
81
+    right: 0;
82
+    bottom: 0;
83
+    width: 32vw;
84
+  }
78 85
 </style>

+ 63
- 17
src/pages/pg2/index.vue 查看文件

@@ -1,17 +1,29 @@
1 1
 <template>
2
-  <div class="page pg-bg pg2" parallax>
3
-    <Bell class="bell abs" />
2
+  <div class="page pg-bg pg2" parallax ref="pgRef">
4 3
     <Cloud class="cloud abs" />
5 4
     <div class="content txt">
6
-      <p class="step animate__animated animate__lightSpeedInRight" data-delay="100">
5
+      <Animate
6
+        comp="p"
7
+        name="animate__lightSpeedInRight"
8
+        delay="100ms"
9
+        :ready="show"
10
+      >
7 11
         1939年 <br >
8 12
         <strong>一抹紫色</strong>在战争的废墟中……
9
-      </p>
10
-      <img class="story step animate__animated animate__fadeInDown" data-delay="600" src="/images/pg2/story.png" alt="">
13
+      </Animate>
14
+      <Animate
15
+        class="story"
16
+        comp="img"
17
+        name="animate__fadeInDown"
18
+        delay="600ms"
19
+        src="/images/pg2/story.png"
20
+        alt=""
21
+        :ready="show"
22
+      />
11 23
     </div>
12 24
 
13 25
     <div class="hill abs">
14
-      <img src="/images/pg2/mountain.png" alt="" parallax-offset="-100">
26
+      <img class="" src="/images/pg2/mountain.png" alt="" parallax-offset="-100">
15 27
     </div>
16 28
     
17 29
     <div class="groups abs">
@@ -19,28 +31,57 @@
19 31
         <img class="stele" src="/images/pg2/stele.png" alt="" parallax-offset="-150">
20 32
         <div parallax-offset="-300" >
21 33
           <img class="ruins" src="/images/pg2/ruins.png" alt="">
22
-          <Smoke class="smoke abs" />
34
+          <!-- <Smoke class="smoke abs" /> -->
23 35
         </div>
24 36
         <img class="persons" src="/images/pg2/persons.png" alt="" parallax-offset="-500">
25 37
       </div>
26 38
     </div>
27 39
 
28
-    <div class="flower abs" parallax-offset="-500">
29
-      <Flower1 class="flower1 abs" />
30
-      <Flower2 class="flower2 abs" />
31
-      <Flower3 class="flower3 abs" />
40
+    <img class="footer abs" src="/images/pg2/footer.png" alt="">
41
+
42
+    <div class="flower abs">
43
+      <Animate class="flower1 abs" :comp="Flower1" :name="flowerAnimate.left" :ready="show" />
44
+      <Animate class="flower2 abs" :comp="Flower2" :name="flowerAnimate.right" :ready="show" />
45
+      <Animate class="flower3 abs" :comp="Flower3" :name="flowerAnimate.left" :ready="show" />
32 46
     </div>
33 47
   </div>
34 48
 </template>
35 49
 
36 50
 <script setup>
37
-import Bell from '@/components/Bell.vue';
38
-import Cloud from './Cloud.vue';
39
-import Smoke from './smoke.vue';
40
-import Flower1 from './flower/Flower1.vue';
41
-import Flower2 from './flower/Flower2.vue';
42
-import Flower3 from './flower/Flower3.vue';
51
+  import { reactive, ref } from 'vue';
52
+  import Bell from '@/components/Bell.vue';
53
+  import Animate from '@/components/Animate.vue';
54
+  import Cloud from './Cloud.vue';
55
+  import Smoke from './smoke.vue';
56
+  import Flower1 from './flower/Flower1.vue';
57
+  import Flower2 from './flower/Flower2.vue';
58
+  import Flower3 from './flower/Flower3.vue';
59
+  import usePageShow from '../usePageShow';
60
+
61
+  const flowerAnimate = reactive({
62
+    left: '',
63
+    right: '',
64
+  });
65
+
66
+  let lastRatio = 0;
67
+  const flowShow = ({ intersectionRatio }) => {
68
+    console.log(intersectionRatio)
69
+    const isIn = intersectionRatio > 0.5 && intersectionRatio > lastRatio;
70
+    const isOut = intersectionRatio < 0.9 && intersectionRatio < lastRatio;
71
+
72
+    if (isIn) {
73
+      flowerAnimate.left = 'animate__fadeInLeft';
74
+      flowerAnimate.right = 'animate__fadeInRight';
75
+    }
76
+    if (isOut) {
77
+      flowerAnimate.left = 'animate__fadeOutLeft';
78
+      flowerAnimate.right = 'animate__fadeOutRight';
79
+    }
80
+
81
+    lastRatio = intersectionRatio;
82
+  }
43 83
 
84
+  const [pgRef, show] = usePageShow(flowShow);
44 85
 </script>
45 86
 
46 87
 <style lang="less" scoped>
@@ -117,6 +158,11 @@ import Flower3 from './flower/Flower3.vue';
117 158
 
118 159
   }
119 160
 
161
+  .footer {
162
+    left: 0;
163
+    bottom: 0;
164
+  }
165
+
120 166
   .flower {
121 167
     bottom: 0;
122 168
     left: 0;

+ 29
- 43
src/pages/pg3/index.vue 查看文件

@@ -1,13 +1,23 @@
1 1
 <template>
2
-  <div class="page pg-bg pg3">
3
-    <Bell class="bell abs" />
4
-    <div class="header step animate__animated animate__fadeInDown" data-delay="100">
2
+  <div class="page pg-bg pg3" ref="pgRef">
3
+    <Animate
4
+      class="header"
5
+      name="animate__zoomIn"
6
+      delay="100ms"
7
+      :ready="show"
8
+    >
5 9
       <img src="/images/pg3/header.png" alt="">
6
-    </div>
10
+    </Animate>
7 11
     <div class="content">
8
-      <p class="txt step animate__animated animate__fadeInDown" data-delay="1000">
12
+      <Animate
13
+        class="txt"
14
+        comp="p"
15
+        name="animate__fadeInDown"
16
+        delay="1s"
17
+        :ready="show"
18
+      >
9 19
         如同“紫金草行动”,我们的身边有很多人,用自己的方式向世界讲述1937,他们不同年龄、不同肤色、不同国家、不同职业,但他们讲述的是全人类共同的心愿。
10
-      </p>
20
+      </Animate>
11 21
     </div>
12 22
 
13 23
     <div class="global-map">
@@ -15,18 +25,18 @@
15 25
     </div>
16 26
 
17 27
     <ul class="card-list">
18
-      <li class="step animate__animated animate__fadeInUp" data-delay="1500">
28
+      <Animate comp="li" name="animate__fadeInUp" delay="1500ms" :ready="show">
19 29
         <img src="/images/pg3/采访1.png" alt="">
20
-      </li>
21
-      <li class="step animate__animated animate__fadeInUp" data-delay="2000">
30
+      </Animate>
31
+      <Animate comp="li" name="animate__fadeInUp" delay="2000ms" :ready="show">
22 32
         <img src="/images/pg3/采访2.png" alt="">
23
-      </li>
24
-      <li class="step animate__animated animate__fadeInUp" data-delay="2500">
33
+      </Animate>
34
+      <Animate comp="li" name="animate__fadeInUp" delay="2500ms" :ready="show">
25 35
         <img src="/images/pg3/采访1.png" alt="">
26
-      </li>
27
-      <li class="step animate__animated animate__fadeInUp" data-delay="3000">
36
+      </Animate>
37
+      <Animate comp="li" name="animate__fadeInUp" delay="3000ms" :ready="show">
28 38
         <img src="/images/pg3/采访2.png" alt="">
29
-      </li>
39
+      </Animate>
30 40
     </ul>
31 41
   </div>
32 42
 </template>
@@ -34,34 +44,10 @@
34 44
 <script setup>
35 45
   import { onBeforeUnmount, onMounted, ref } from 'vue';
36 46
   import Bell from '@/components/Bell.vue';
37
-  import Erasure from '@/components/Erasure.vue';
38
-
39
-  const pgRef = ref();
40
-  const show = ref(false);
41
-  let intersectionObserver = null;
42
-
43
-  const onAnimationEnd = () => {
44
-    show2.value = true;
45
-  }
46
-
47
-  // onMounted(() => {
48
-  //   intersectionObserver = new IntersectionObserver((entries) => {
49
-  //     // console.log(entries[0]);
50
-  //     const { intersectionRatio } = entries[0];
51
-  //     if (intersectionRatio <= 0) {
52
-  //       show.value = false;
53
-  //     } else if (intersectionRatio >= 0.5)  {
54
-  //       show.value = true;
55
-  //     }
56
-  //   }, {threshold: [0.0, 0.5]});
57
-
58
-  //   intersectionObserver.observe(pgRef.value);
59
-  // })
60
-
61
-  // onBeforeUnmount(() => {
62
-  //   intersectionObserver.disconnect();
63
-  // })
47
+  import Animate from '@/components/Animate.vue';
48
+  import usePageShow from '../usePageShow';
64 49
 
50
+  const [pgRef, show] = usePageShow(0.3);
65 51
 </script>
66 52
 
67 53
 
@@ -75,9 +61,9 @@
75 61
   }
76 62
 
77 63
   .header {
78
-    width: 20vw;
64
+    width: 30vw;
79 65
     margin: auto;
80
-    margin-top: 60px;
66
+    margin-top: 30px;
81 67
     position: relative;
82 68
 
83 69
     &::before {

+ 24
- 0
src/pages/useIntersection.js 查看文件

@@ -0,0 +1,24 @@
1
+import { onBeforeUnmount, onMounted } from 'vue';
2
+
3
+export default function useIntersection(elRef, callback) {
4
+  let intersectionObserver = null;
5
+
6
+  onMounted(() => {
7
+    intersectionObserver = new IntersectionObserver((entries) => {
8
+      // console.log(entries[0]);
9
+      callback(entries[0])
10
+      // const { intersectionRatio } = entries[0];
11
+      // if (intersectionRatio <= 0) {
12
+      //   show.value = false;
13
+      // } else if (intersectionRatio >= 0.5)  {
14
+      //   show.value = true;
15
+      // }
16
+    }, {threshold: [0.0, 0.1, 0.5]});
17
+
18
+    intersectionObserver.observe(elRef.value);
19
+  })
20
+
21
+  onBeforeUnmount(() => {
22
+    intersectionObserver.disconnect();
23
+  })
24
+}

+ 40
- 0
src/pages/usePageShow.js 查看文件

@@ -0,0 +1,40 @@
1
+import { onBeforeUnmount, onMounted, ref } from 'vue';
2
+
3
+export default function useIntersection(param) {
4
+  const pageRef = ref();
5
+  const showRef = ref(false);
6
+  let intersectionObserver = null;
7
+
8
+  let breakRatio = 0.5;
9
+  if (typeof param === 'number') {
10
+    breakRatio = param;
11
+  }
12
+
13
+  let callback = null;
14
+  if (typeof param === 'function') {
15
+    callback = param;
16
+  }
17
+
18
+  onMounted(() => {
19
+    intersectionObserver = new IntersectionObserver((entries) => {
20
+      const { intersectionRatio } = entries[0];
21
+      if (intersectionRatio <= 0.1) {
22
+        showRef.value = false;
23
+      } else if (intersectionRatio >= breakRatio)  {
24
+        showRef.value = true;
25
+      }
26
+
27
+      if (typeof callback === 'function') {
28
+        callback(entries[0]);
29
+      }
30
+    }, {threshold: [0.0, 0.1, 0.3, 0.5, 0.8, 0.9]});
31
+
32
+    intersectionObserver.observe(pageRef.value);
33
+  })
34
+
35
+  onBeforeUnmount(() => {
36
+    intersectionObserver.disconnect();
37
+  })
38
+
39
+  return [pageRef, showRef];
40
+}

+ 13
- 0
src/store/index.js 查看文件

@@ -3,19 +3,32 @@ import createStore from '@zjxpcyc/vue-tiny-store';
3 3
 
4 4
 const useAudio = () => {
5 5
   const el = ref();
6
+  const state = ref(0);
6 7
 
7 8
   const play = () => {
8 9
     el.value.play();
10
+    state.value = 1;
9 11
   }
10 12
 
11 13
   const pause = () => {
12 14
     el.value.pause();
15
+    state.value = 0;
16
+  }
17
+  
18
+  const toggle = () => {
19
+    if (state.value) {
20
+      pause();
21
+    } else {
22
+      play();
23
+    }
13 24
   }
14 25
 
15 26
   return {
16 27
     el,
28
+    state,
17 29
     play,
18 30
     pause,
31
+    toggle,
19 32
   }
20 33
 };
21 34