Commit cc5d4896 authored by hucy's avatar hucy

fix:动画

parent 7740490e
......@@ -12,15 +12,19 @@
"@quasar/extras": "^1.0.0",
"ag-grid-community": "^28.0.0",
"ag-grid-vue3": "^28.0.0",
"animejs": "^3.2.1",
"axios": "^0.27.2",
"core-js": "^3.6.5",
"decimal.js": "^10.4.2",
"echarts": "^5.3.2",
"gsap": "^3.11.3",
"hover-effect": "^1.1.0",
"lodash-es": "^4.17.21",
"mitt": "^3.0.0",
"pinia": "^2.0.14",
"quasar": "^2.6.0",
"swiper": "^8.4.5",
"three": "^0.147.0",
"vue": "^3.0.0",
"vue-i18n": "^9.2.2",
"vue-router": "^4.0.0",
......@@ -3292,6 +3296,11 @@
"ajv": "^6.9.1"
}
},
"node_modules/animejs": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/animejs/-/animejs-3.2.1.tgz",
"integrity": "sha512-sWno3ugFryK5nhiDm/2BKeFCpZv7vzerWUcUPyAZLDhMek3+S/p418ldZJbJXo5ZUOpfm2kP2XRO4NJcULMy9A=="
},
"node_modules/ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
......@@ -6491,6 +6500,11 @@
"dev": true,
"license": "ISC"
},
"node_modules/gsap": {
"version": "3.11.3",
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.11.3.tgz",
"integrity": "sha512-xc/iIJy+LWiMbRa4IdMtdnnKa/7PXEK6NNzV71gdOYUVeTZN7UWnLU0fB7Hi1iwiz4ZZoYkBZPPYGg+2+zzFHA=="
},
"node_modules/gzip-size": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
......@@ -6580,6 +6594,25 @@
"he": "bin/he"
}
},
"node_modules/hover-effect": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/hover-effect/-/hover-effect-1.1.0.tgz",
"integrity": "sha512-172ezjW88PYOHEerHzQWmkMXaAx4hOerE+BQ3iAN+n3Xza+TczuJKgkYlCaHhcGFUdG7is/1UcepCJECNeFscg==",
"dependencies": {
"gsap": "^2.0.2",
"three": "^0.99.0"
}
},
"node_modules/hover-effect/node_modules/gsap": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/gsap/-/gsap-2.1.3.tgz",
"integrity": "sha512-8RFASCqi2FOCBuv7X4o7M6bLdy+1hbR0azg+MG7zz+EVsI+OmJblYsTk0GEepQd2Jg/ItMPiVTibF7r3EVxjZQ=="
},
"node_modules/hover-effect/node_modules/three": {
"version": "0.99.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.99.0.tgz",
"integrity": "sha512-DmNNq6H6nRGaqxScJ8x7v5VjdtDZR72oTVwDdKbB2BYNFxCkAoo9vdFAznEsMu9YzTV2yFvbVs7qHRzvJZzTIg=="
},
"node_modules/hpack.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
......@@ -10528,6 +10561,11 @@
"dev": true,
"license": "MIT"
},
"node_modules/three": {
"version": "0.147.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.147.0.tgz",
"integrity": "sha512-LPTOslYQXFkmvceQjFTNnVVli2LaVF6C99Pv34fJypp8NbQLbTlu3KinZ0zURghS5zEehK+VQyvWuPZ/Sm8fzw=="
},
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
......@@ -13954,6 +13992,11 @@
"dev": true,
"requires": {}
},
"animejs": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/animejs/-/animejs-3.2.1.tgz",
"integrity": "sha512-sWno3ugFryK5nhiDm/2BKeFCpZv7vzerWUcUPyAZLDhMek3+S/p418ldZJbJXo5ZUOpfm2kP2XRO4NJcULMy9A=="
},
"ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
......@@ -16132,6 +16175,11 @@
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
"dev": true
},
"gsap": {
"version": "3.11.3",
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.11.3.tgz",
"integrity": "sha512-xc/iIJy+LWiMbRa4IdMtdnnKa/7PXEK6NNzV71gdOYUVeTZN7UWnLU0fB7Hi1iwiz4ZZoYkBZPPYGg+2+zzFHA=="
},
"gzip-size": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
......@@ -16189,6 +16237,27 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
"hover-effect": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/hover-effect/-/hover-effect-1.1.0.tgz",
"integrity": "sha512-172ezjW88PYOHEerHzQWmkMXaAx4hOerE+BQ3iAN+n3Xza+TczuJKgkYlCaHhcGFUdG7is/1UcepCJECNeFscg==",
"requires": {
"gsap": "^2.0.2",
"three": "^0.99.0"
},
"dependencies": {
"gsap": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/gsap/-/gsap-2.1.3.tgz",
"integrity": "sha512-8RFASCqi2FOCBuv7X4o7M6bLdy+1hbR0azg+MG7zz+EVsI+OmJblYsTk0GEepQd2Jg/ItMPiVTibF7r3EVxjZQ=="
},
"three": {
"version": "0.99.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.99.0.tgz",
"integrity": "sha512-DmNNq6H6nRGaqxScJ8x7v5VjdtDZR72oTVwDdKbB2BYNFxCkAoo9vdFAznEsMu9YzTV2yFvbVs7qHRzvJZzTIg=="
}
}
},
"hpack.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
......@@ -18845,6 +18914,11 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
"three": {
"version": "0.147.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.147.0.tgz",
"integrity": "sha512-LPTOslYQXFkmvceQjFTNnVVli2LaVF6C99Pv34fJypp8NbQLbTlu3KinZ0zURghS5zEehK+VQyvWuPZ/Sm8fzw=="
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
......
......@@ -86,7 +86,12 @@ module.exports = configure(function (ctx) {
proxy: {},
},
extras: ['material-icons', 'fontawesome-v6', 'bootstrap-icons'],
extras: [
'material-icons',
'ionicons-v4',
'fontawesome-v6',
'bootstrap-icons',
],
// https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-framework
framework: {
......
......@@ -171,6 +171,13 @@ export default defineComponent({
link: '/page8',
active: false,
},
{
title: '九',
caption: '动画2',
icon: require('./menuListIcons/page8.svg'),
link: '/page9',
active: false,
},
];
for (const item of lists) {
......
......@@ -22,30 +22,37 @@ export const X_DATA = [
'2022-11-28',
'2022-11-29',
'2022-11-30',
'2022-12-01',
'2022-12-02',
'2022-12-03',
'2022-12-04',
'2022-12-05',
'2022-12-06',
'2022-12-07',
];
// 本土感染者
export const LOCAL_INFECTED = [
30, 35, 50, 48, 57, 99, 133, 100, 121, 176, 211, 200, 316, 383, 473, 537, 591,
735, 770, 910, 829, 705, 764,
735, 770, 910, 829, 705, 764, 516, 476, 462, 435, 339, 277, 162,
];
// 省感染者
export const PROVINCE_INNER_INFECTED = [
16, 22, 28, 29, 40, 79, 115, 82, 107, 157, 192, 184, 280, 346, 428, 509, 547,
690, 718, 853, 763, 660, 716,
690, 718, 853, 763, 660, 716, 501, 448, 430, 402, 334, 271, 159,
];
// 外省来(返)蓉感染者
export const PROVINCE_OUT_INFECTED = [
14, 13, 22, 19, 17, 20, 18, 18, 14, 19, 19, 16, 36, 37, 45, 28, 44, 45, 52,
57, 66, 45, 48,
57, 66, 45, 48, 15, 28, 32, 33, 5, 6, 3,
];
// 境外输入感染者
export const OFFSHORE_INPUT_INFECTED = [
7, 15, 26, 10, 5, 9, 9, 8, 21, 17, 17, 12, 20, 8, 13, 11, 13, 22, 16, 13, 15,
16, 20,
16, 20, 8, 29, 21, 23, 26, 17, 26,
];
export const LABEL_DATA = [
......@@ -54,4 +61,4 @@ export const LABEL_DATA = [
'外省来(返)蓉感染者',
'境外输入感染者',
];
export const COLOR_LIST = ['#E8110F', '#f29c2b', '#eec60a', '#95E1D3'];
export const COLOR_LIST = ['#FF6384', '#FF9F40', '#FFCD56', '#4BC0C0'];
<!--
* 草稿
* 动画
* https://www.youtube.com/@OnlineTutorialsYT/playlists
-->
<script setup lang="ts">
// import { ref } from 'vue';
import LoadingCircle from './element/LoadingCircle.vue';
import ButtonHover from './element/ButtonHover.vue';
import Text3D from './element/Text3D.vue';
import LightedText from './element/LightedText.vue';
// import LightedText from './element/LightedText.vue';
import Rotate3D from './element/Rotate3D.vue';
import TypewritersText from './element/TypewritersText.vue';
import BoderAnimation from './element/BoderAnimation.vue';
import RangeSlider from './element/RangeSlider.vue';
import SearchInput from './element/SearchInput.vue';
import SwiperCard from './element/SwiperCard.vue';
import DropDownMenu from './element/DropDownMenu.vue';
import MagicMenuIndicator from './element/MagicMenuIndicator.vue';
</script>
<template>
<div class="box">
......@@ -29,7 +32,8 @@ import SwiperCard from './element/SwiperCard.vue';
<div class="column items-center">
<div class="my-card" style="width: 460px; height: 160px">
<lighted-text />
<!-- <lighted-text /> -->
这个有点儿卡
</div>
<div class="my-card" style="height: 140px">
<typewriters-text />
......@@ -49,9 +53,13 @@ import SwiperCard from './element/SwiperCard.vue';
<div class="my-card" style="width: 480px">
<search-input />
</div>
<div class="my-card"></div>
<div class="my-card" style="width: 540px">
<magic-menu-indicator />
</div>
<div class="row no-wrap">
<div class="my-card"></div>
<div class="my-card" style="height: 500px">
<drop-down-menu />
</div>
<div class="my-card" style="width: 900px; height: 500px">
<swiper-card />
......@@ -77,9 +85,9 @@ import SwiperCard from './element/SwiperCard.vue';
justify-content: center;
align-items: center;
overflow: hidden;
transition: all 0.4s;
transition: all 0.5s;
&:hover {
box-shadow: rgb(38, 57, 77) 0px 20px 30px -10px;
box-shadow: rgba(0, 0, 0, 0.4) 0px 30px 90px;
// scale: 1.1;
z-index: 99;
}
......
<script setup lang="ts">
import { ref } from 'vue';
const isActive = ref(false);
function clickMenu() {
isActive.value = !isActive.value;
}
</script>
<template>
<div class="content fit center">
<div class="navigation" :class="{ active: isActive }">
<div class="user-box">
<div class="img-box">
<img src="https://cdn.quasar.dev/img/avatar2.jpg" alt="" />
</div>
<p class="username">User name</p>
</div>
<div class="menu-toggle" @click="clickMenu"></div>
<ul class="menu">
<li>
<a href="javascript:volid(0);"
><i class="bi bi-person"></i>My Profile</a
>
</li>
<li>
<a href="javascript:volid(0);"
><i class="bi bi-chat-dots"></i>Messages</a
>
</li>
<li>
<a href="javascript:volid(0);"
><i class="bi bi-bell"></i>Notification</a
>
</li>
<li>
<a href="javascript:volid(0);"><i class="bi bi-gear"></i>Settings</a>
</li>
<li>
<a href="javascript:volid(0);"
><i class="bi bi-question"></i>Help & Support</a
>
</li>
<li>
<a href="javascript:volid(0);"
><i class="bi bi-box-arrow-right"></i>Logout</a
>
</li>
</ul>
</div>
</div>
</template>
<style lang="scss" scoped>
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Ubuntu', sans-serif;
}
.content {
background: linear-gradient(45deg, #4e65ff, #92effd);
position: relative;
}
.navigation {
position: absolute;
top: 20px;
right: 20px;
width: 120px;
height: 60px;
background-color: #fff;
box-shadow: 0 25px 35px rgba(0, 0, 0, 0.1);
display: flex;
justify-content: space-between;
transition: height 0.5s, width 0.5s;
transition-delay: 0s, 0.75s;
overflow: hidden;
.user-box {
position: relative;
width: 60px;
height: 60px;
background-color: #fff;
display: flex;
align-items: center;
overflow: hidden;
transition: 0.5s;
transition-delay: 0.5s;
.img-box {
position: relative;
min-width: 60px;
height: 60px;
background-color: #000;
border-radius: 50%;
border: 10px solid #fff;
overflow: hidden;
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
}
.username {
white-space: nowrap;
color: #555;
font-size: 1.1em;
}
}
.menu-toggle {
position: relative;
width: 60px;
height: 60px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
&::before {
position: absolute;
content: '';
width: 32px;
height: 2px;
background-color: #555;
transform: translateY(-10px);
box-shadow: 0 10px #555;
transition: 0.5s;
}
&::after {
position: absolute;
content: '';
width: 32px;
height: 2px;
background-color: #555;
transform: translateY(10px);
transition: 0.5s;
}
}
}
.active {
width: 300px;
height: 400px;
transition: width 0.5s, height 0.5s;
transition-delay: 0s, 0.75s;
.menu-toggle {
&::before {
transform: translateY(0) rotate(45deg);
box-shadow: 0 0 #555;
}
&::after {
transform: translateY(0) rotate(-45deg);
box-shadow: 0 0 #555;
}
}
.user-box {
width: calc(100% - 60px);
transition-delay: 0s;
}
}
.menu {
position: absolute;
width: 100%;
height: calc(100% - 60px);
margin-top: 60px;
padding: 20px;
border-top: 1px solid rgba(0, 0, 0, 0.1);
li {
list-style: none;
a {
display: flex;
align-items: center;
margin: 18px 0;
font-size: 1em;
gap: 10px;
text-decoration: none;
color: #555;
i {
font-size: 1.5em;
}
&:hover {
color: #4e65ff;
}
}
}
}
</style>
<script setup lang="ts">
import { ref, reactive } from 'vue';
const activeLink = ref(1);
const menuList = reactive([
{
id: 1,
icon: 'bi bi-house-door',
text: 'Home',
},
{
id: 2,
icon: 'bi bi-person',
text: 'Profile',
},
{
id: 3,
icon: 'bi bi-chat',
text: 'Mesaage',
},
{
id: 4,
icon: 'bi bi-camera',
text: 'Photos',
},
{
id: 5,
icon: 'bi bi-gear',
text: 'Settings',
},
]);
function clickList(val: number) {
activeLink.value = val;
}
</script>
<template>
<div class="content fit center">
<div class="navigation">
<ul>
<li
class="list"
:class="{ active: item.id === activeLink }"
@click="clickList(item.id)"
v-for="item in menuList"
:key="item.id"
>
<a href="javascript:volid(0);">
<span class="icon"><i :class="item.icon"></i></span>
<span class="text">{{ item.text }}</span>
</a>
</li>
<div class="indicator"></div>
</ul>
</div>
</div>
</template>
<style lang="scss" scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.content {
background-color: #222327;
}
.navigation {
width: 400px;
height: 70px;
background-color: #fff;
position: relative;
display: flex;
justify-content: center;
align-items: center;
border-radius: 10px;
ul {
display: flex;
width: 350px;
li {
position: relative;
list-style: none;
width: 70px;
height: 70px;
z-index: 1;
a {
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
text-align: center;
font-weight: 500;
.icon {
position: relative;
display: block;
line-height: 75px;
font-size: 1.5em;
text-align: center;
transition: 0.5s;
color: #222327;
}
.text {
position: absolute;
color: #222327;
font-weight: 400;
font-size: 0.75em;
letter-spacing: 0.05em;
opacity: 0;
transform: translateY(20px);
transition: 0.5s;
}
}
&.active {
a .icon {
transform: translateY(-36px);
}
a .text {
opacity: 1;
transform: translateY(10px);
}
}
}
}
}
.indicator {
position: absolute;
top: -50%;
width: 70px;
height: 70px;
border-radius: 50%;
background-color: #29fd53;
border: 6px solid #222327;
transition: 0.5s;
&::before {
position: absolute;
content: '';
top: 50%;
left: -22px;
width: 20px;
height: 20px;
border-top-right-radius: 20px;
box-shadow: 0px -10px 0 0 #222327;
background-color: transparent;
}
&::after {
position: absolute;
content: '';
top: 50%;
right: -22px;
width: 20px;
height: 20px;
border-top-left-radius: 20px;
box-shadow: 0px -10px 0 0 #222327;
background-color: transparent;
}
}
.navigation ul li:nth-child(1).active ~ .indicator {
transform: translateX(calc(70px * 0));
}
.navigation ul li:nth-child(2).active ~ .indicator {
transform: translateX(calc(70px * 1));
}
.navigation ul li:nth-child(3).active ~ .indicator {
transform: translateX(calc(70px * 2));
}
.navigation ul li:nth-child(4).active ~ .indicator {
transform: translateX(calc(70px * 3));
}
.navigation ul li:nth-child(5).active ~ .indicator {
transform: translateX(calc(70px * 4));
}
</style>
......@@ -16,7 +16,7 @@ onMounted(() => {
modifier: 2,
slideShadows: true,
},
loop: true,
loop: false,
});
});
</script>
......@@ -148,14 +148,14 @@ onMounted(() => {
<style lang="scss" scoped>
.content {
:deep(.swiper-3d .swiper-slide-shadow-left) {
background-image: none;
}
:deep(.swiper-3d .swiper-slide-shadow-right) {
background-image: none;
}
// :deep(.swiper-3d .swiper-slide-shadow-left) {
// background-image: none;
// }
// :deep(.swiper-3d .swiper-slide-shadow-right) {
// background-image: none;
// }
:deep(.swiper-slide-active) {
filter: blur(0);
// filter: blur(0);
background-color: #fff;
}
}
......@@ -167,7 +167,7 @@ onMounted(() => {
justify-content: center;
align-items: center;
overflow: hidden;
background-color: #7abed1;
background-color: #9f6b65;
}
.swiper {
width: 100%;
......@@ -184,7 +184,7 @@ onMounted(() => {
background-color: #d1ebff;
border-radius: 10px;
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.2);
filter: blur(4px);
// filter: blur(4px);
}
.mycard {
box-sizing: border-box;
......@@ -200,14 +200,14 @@ onMounted(() => {
.details {
display: flex;
align-items: center;
justify-content: end;
justify-content: flex-end;
margin-top: 10px;
h3 {
font-size: 16px;
font-weight: 400;
letter-spacing: 1px;
color: #7abed1;
color: #9f6b65;
line-height: 1.1em;
text-align: right;
margin-bottom: 0px;
......
<!--
* 动画2
* https://www.youtube.com/@OnlineTutorialsYT/playlists
-->
<script setup lang="ts">
import { ref } from 'vue';
import TextVue from './element/TextVue.vue';
import WorkingDigitalClock from './element/WorkingDigitalClock .vue';
import SvgLineDrawingAnimation from './element/SvgLineDrawingAnimation.vue';
import FlyingTextAnimationEffects from './element/FlyingTextAnimationEffects.vue';
import TransformationAnimation from './element/TransformationAnimation.vue';
import ImageDistortionHoverEffects from './element/ImageDistortionHoverEffects.vue';
const listData = [
{
title: '测试',
name: 'text-vue',
},
{
title: 'SVG 数字时钟',
name: 'working-digital-clock',
},
{
title: 'SVG 画线动画',
name: 'svg-line-drawing-animation',
},
{
title: '飞行文本动画效果 ',
name: 'flying-text-animation-effects',
},
{
title: '变形动画',
name: 'transformation-animation',
},
{
title: '图像悬停扭曲效果',
name: 'image-distortion-hover-effects',
},
];
const isShow = ref(true);
const elementName = ref('image-distortion-hover-effects');
const elementTitle = ref('');
function onclick(data: any) {
elementTitle.value = data.title;
elementName.value = data.name;
isShow.value = true;
}
function goBack() {
isShow.value = false;
}
</script>
<template>
<div>
<div v-if="isShow" class="main">
<div class="action">
<q-btn
@click="goBack"
round
flat
icon="bi-arrow-left"
size="12px"
style="color: #555"
/>
<span>{{ elementTitle }}</span>
</div>
<div class="main-content">
<text-vue v-if="elementName === 'text-vue'" />
<working-digital-clock v-if="elementName === 'working-digital-clock'" />
<svg-line-drawing-animation
v-if="elementName === 'svg-line-drawing-animation'"
/>
<flying-text-animation-effects
v-if="elementName === 'flying-text-animation-effects'"
/>
<transformation-animation
v-if="elementName === 'transformation-animation'"
/>
<image-distortion-hover-effects
v-if="elementName === 'image-distortion-hover-effects'"
/>
</div>
</div>
<div v-else>
<ul class="list">
<li
@click="onclick(item)"
v-for="(item, index) in listData"
:key="index"
>
<p class="title">
{{ item.title }}
</p>
</li>
</ul>
</div>
</div>
</template>
<style lang="scss" scoped>
.list {
padding: 20px;
li {
list-style: none;
.title {
cursor: pointer;
font-size: 16px;
color: #555;
&:hover {
text-decoration: underline;
}
}
}
}
.main {
.action {
height: 40px;
background-color: #fff;
line-height: 40px;
box-sizing: border-box;
}
.main-content {
position: relative;
height: calc(100vh - 90px);
}
}
</style>
<!--
* 飞行文本动画效果
-->
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import anime from 'animejs/lib/anime.es.js';
const textRef = ref<any>(null);
onMounted(() => {
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
// `\S`匹配一个非空白字符。
textRef.value.innerHTML = textRef.value.textContent.replace(
/\S/g,
'<span>$&</span>'
);
const animation = anime.timeline({
targets: '.text span',
easing: 'easeInOutExpo',
loop: true,
});
animation
.add({
rotate: function () {
return anime.random(-360, 360);
},
translateX: function () {
return anime.random(-500, 500);
},
translateY: function () {
return anime.random(-500, 500);
},
duration: 5000,
delay: anime.stagger(20),
})
.add({
rotate: 0,
translateX: 0,
translateY: 0,
duration: 5000,
delay: anime.stagger(20),
});
});
</script>
<template>
<div class="box">
<!-- https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/section -->
<section>
<p class="text" ref="textRef">
你说我在做梦吗?人生如梦,我投入的却是真情。世界先爱了我,我不能不爱它。
只记花开不记人,你在花里,如花在风中。
那一年,花开得不是最好,可是还好,我遇到你;那一年,花开得好极了,好像专是为了你;那一年,花开得很迟,还好,有你。、“什么事都轻描淡写,毫不装腔作势。说话自然也流露出得意,可是得意中有还有一点对于自己的嘲讽。这是一点本事。可是人最好没有这点本事。他正因为有这些本事,才种种不如别人。”
</p>
</section>
</div>
</template>
<style lang="scss" scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.box {
width: 100%;
height: 100%;
}
section {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #130e23;
.text {
position: relative;
text-align: center;
color: #00cefe;
margin: 40px;
max-width: 650px;
}
}
:deep(section .text span) {
position: relative;
display: inline-block;
}
</style>
<!--
* 图像悬停扭曲效果
* https://www.youtube.com/@OnlineTutorialsYT/playlists
* https://www.youtube.com/watch?v=ZcJ4ANTP8zk&list=PL5e68lK9hEzdwblnDobNUCJKm6mFoWP8m&index=2
* https://github.com/robin-dela/hover-effect
-->
<script setup lang="ts">
import { onMounted } from 'vue';
import hoverEffect from 'hover-effect';
onMounted(() => {
Array.from(document.querySelectorAll('.img-box')).forEach((e) => {
const imgs = Array.from(e.querySelectorAll('img'));
new hoverEffect({
parent: e,
intensity: 0.3,
image1: imgs[0].getAttribute('src'),
image2: imgs[1].getAttribute('src'),
displacementImage: imgs[0].getAttribute('src'),
});
});
});
</script>
<template>
<div class="box">
<div class="container">
<div class="img-box">
<img src="../imgs/2.jpg" alt="" />
<img src="../imgs/4.jpg" alt="" />
</div>
<div class="img-box">
<img src="../imgs/5.jpg" alt="" />
<img src="../imgs/6.jpg" alt="" />
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.box {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.container {
position: relative;
width: 1480px;
display: flex;
justify-content: space-between;
align-items: center;
.img-box {
position: relative;
width: 700px;
height: 400px;
margin: 20px;
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
// https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-fit
object-fit: cover;
display: none;
}
}
}
</style>
This diff is collapsed.
<!--
* 测试
-->
<script setup lang="ts">
import { onMounted } from 'vue';
import anime from 'animejs/lib/anime.es.js';
onMounted(() => {
anime({
targets: '.box1',
left: '240px',
backgroundColor: '#000',
borderRadius: ['0%', '50%'],
easing: 'easeInOutQuad',
});
let path = anime.path('path');
anime({
targets: '.el',
translateX: path('x'),
translateY: path('y'),
rotate: path('angle'),
easing: 'linear',
duration: 2000,
loop: false,
});
});
</script>
<template>
<div>
<div class="box1"></div>
<div class="text-svg">
<div class="el"></div>
<svg
t="1652088790468"
class="icon"
viewBox="0 0 1067 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="1324"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="520.99609375"
height="500"
>
<path
d="M43.389831 980.610169V173.559322c0-71.59322 58.576271-130.169492 130.169491-130.169491h720.271186c71.59322 0 130.169492 58.576271 130.169492 130.169491v807.050847H43.389831z"
fill="#94D6D0"
p-id="1325"
></path>
<path
d="M43.389831 251.661017h980.610169v728.949152H43.389831z"
fill="#F0F0FF"
p-id="1326"
></path>
<path
d="M648.647593 521.719322l91.118644-60.745763-50.267118-31.678915-56.40678 394.847458 64.433898 9.207322 56.40678-394.847458 10.292068-72.053152-60.559187 40.374237-91.118644 60.745763zM305.173695 592.271186h199.59322v65.084746h-199.59322z"
fill="#ABABD1"
p-id="1327"
></path>
<path
d="M372.427932 514.169492h65.084746v221.288135h-65.084746z"
fill="#ABABD1"
p-id="1328"
></path>
<path
d="M43.389831 922.033898h980.610169V980.610169H43.389831z"
fill="#6E6E96"
opacity=".07"
p-id="1329"
></path>
<path
d="M379.661017 43.389831h333.377085v208.271186H379.661017z"
fill="#FFE95F"
p-id="1330"
></path>
<path
d="M1024 173.559322c0-71.59322-58.576271-130.169492-130.169492-130.169491h-210.440677v208.271186H1024V173.559322z"
fill="#FF9780"
p-id="1331"
></path>
<path
d="M43.389831 208.813559h980.610169v83.525424H43.389831z"
fill="#6E6E96"
opacity=".15"
p-id="1332"
></path>
</svg>
</div>
<div class="text-router"></div>
</div>
</template>
<style lang="scss" scoped>
.box1 {
left: 0;
position: relative;
width: 100px;
height: 100px;
background-color: pink;
}
.text-svg {
position: relative;
margin-bottom: 40px;
.el {
position: absolute;
top: 0;
left: 0;
width: 50px;
height: 50px;
background-color: pink;
}
}
.text-router {
box-sizing: border-box;
width: 500px;
height: 500px;
background-color: rgb(192, 230, 255);
position: relative;
}
</style>
<!--
* 变形动画
-->
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import anime from 'animejs/lib/anime.es.js';
const containerRef = ref<any>(null);
onMounted(() => {
for (let i = 1; i <= 100; i++) {
const dot = document.createElement('div');
dot.classList.add('element');
containerRef.value.appendChild(dot);
}
const dotAll = document.querySelectorAll('.element');
const animation = anime.timeline({
targets: dotAll,
easing: 'easeInOutExpo',
delay: anime.stagger(100, { grid: [10, 10], from: 'center' }),
loop: true,
});
animation
.add({
rotateZ: 180,
translateY: anime.stagger(-20, {
grid: [10, 10],
from: 'center',
axis: 'y',
}),
translateX: anime.stagger(-20, {
grid: [10, 10],
from: 'center',
axis: 'x',
}),
opacity: 1,
})
.add({
borderRadius: 50,
})
.add({
scale: 0.2,
opacity: 0.2,
})
.add({
rotateZ: 180,
translateY: anime.stagger(0, {
grid: [10, 10],
from: 'center',
axis: 'y',
}),
translateX: anime.stagger(0, {
grid: [10, 10],
from: 'center',
axis: 'x',
}),
opacity: 1,
})
.add({
scale: 1,
borderRadius: 0,
})
.add({
rotateZ: -90,
});
});
</script>
<template>
<div class="box">
<div class="container" ref="containerRef"></div>
</div>
</template>
<style lang="scss" scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:deep(.container .element) {
position: relative;
width: 40px;
height: 40px;
background-color: #fff;
scale: 0.75;
}
.box {
width: 100%;
height: 100%;
background-color: #222;
display: flex;
justify-content: center;
align-items: center;
}
.container {
position: relative;
min-width: 400px;
width: 400px;
height: 400px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
}
</style>
<!--
* 时钟
-->
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
const hoursRef = ref<any>(null);
const minutesRef = ref<any>(null);
const secondsRef = ref<any>(null);
const ampmRef = ref<any>(null);
const hhRef = ref<any>(null);
const mmRef = ref<any>(null);
const ssRef = ref<any>(null);
const hrDotRef = ref<any>(null);
const minDotRef = ref<any>(null);
const secDotRef = ref<any>(null);
const hours = ref<any>(0);
const minutes = ref<any>(0);
const seconds = ref<any>(0);
const ampm = ref('');
const timer = ref<any>(null);
onMounted(() => {
getTime();
});
onBeforeUnmount(() => {
clearTimeout(timer.value);
timer.value = null;
});
function getTime() {
let h = new Date().getHours() as any;
let m = new Date().getMinutes() as any;
let s = new Date().getSeconds() as any;
let am = h >= 12 ? 'PM' : 'AM';
if (h > 12) {
h = h - 12;
}
if (h < 10) {
h = '0' + h;
}
if (m < 10) {
m = '0' + m;
}
if (s < 10) {
s = '0' + s;
}
hours.value = h;
minutes.value = m;
seconds.value = s;
ampm.value = am;
hhRef.value.style.strokeDashoffset = 440 - (440 * h) / 12;
mmRef.value.style.strokeDashoffset = 440 - (440 * m) / 60;
ssRef.value.style.strokeDashoffset = 440 - (440 * s) / 60;
// 360/12 = 30
hrDotRef.value.style.transform = `rotate(${h * 30}deg)`;
// 360/60 = 6
minDotRef.value.style.transform = `rotate(${m * 6}deg)`;
// 360/60 = 6
secDotRef.value.style.transform = `rotate(${s * 6}deg)`;
timer.value = setTimeout(() => {
getTime();
}, 1000);
}
</script>
<template>
<div class="fit box">
<div ref="timeRef" id="time">
<div class="circle" style="--clr: #ff2971">
<div class="dots" ref="hrDotRef"></div>
<svg>
<circle cx="70" cy="70" r="70"></circle>
<circle cx="70" cy="70" r="70" id="hh" ref="hhRef"></circle>
</svg>
<div id="hours" ref="hoursRef">
{{ hours }} <br /><span>Hours</span>
</div>
</div>
<div class="circle" style="--clr: #fee800">
<div class="dots" ref="minDotRef"></div>
<svg>
<circle cx="70" cy="70" r="70"></circle>
<circle cx="70" cy="70" r="70" id="mm" ref="mmRef"></circle>
</svg>
<div id="minutes" ref="minutesRef">
{{ minutes }} <br /><span>Minutes</span>
</div>
</div>
<div class="circle" style="--clr: #04fc43">
<div class="dots" ref="secDotRef"></div>
<svg>
<circle cx="70" cy="70" r="70"></circle>
<circle cx="70" cy="70" r="70" id="ss" ref="ssRef"></circle>
</svg>
<div id="seconds" ref="secondsRef">
{{ seconds }} <br /><span>Seconds</span>
</div>
</div>
<div class="ap">
<div id="ampm" ref="ampmRef">{{ ampm }}</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.box {
display: flex;
justify-content: center;
align-items: center;
background-color: #2f363e;
}
#time {
display: flex;
gap: 40px;
color: #fff;
.circle {
position: relative;
width: 150px;
height: 150px;
display: flex;
justify-content: center;
align-items: center;
svg {
position: relative;
width: 150px;
height: 150px;
transform: rotate(270deg);
circle {
width: 100%;
height: 100%;
fill: transparent;
stroke: #191919;
stroke-width: 4;
transform: translate(5px, 5px);
&:nth-child(2) {
stroke: var(--clr);
stroke-dasharray: 440;
}
}
}
.dots {
position: absolute;
width: 100%;
height: 100%;
z-index: 10;
display: flex;
justify-content: center;
&::before {
position: absolute;
content: '';
top: -3px;
width: 15px;
height: 15px;
background-color: var(--clr);
border-radius: 50%;
box-shadow: 0 0 20px var(--clr), 0 0 60px var(--clr);
}
}
}
div {
position: absolute;
text-align: center;
font-weight: 500;
font-size: 1.5em;
span {
position: absolute;
font-size: 0.35em;
font-weight: 300;
letter-spacing: 0.1em;
transform: translateX(-50%);
text-transform: uppercase;
}
}
.ap {
position: relative;
font-size: 1em;
transform: translateX(-20px);
}
}
</style>
This diff is collapsed.
......@@ -92,6 +92,16 @@ const routes: RouteRecordRaw[] = [
keepalive: true,
},
},
{
path: 'page9',
name: 'PAGE9',
component: () => import('../modules/page9/IndexPage.vue'),
meta: {
title: '动画2',
permission: ['*'],
keepalive: true,
},
},
],
},
],
......
declare module 'animejs/lib/anime.es.js';
declare module 'swiper/bundle';
......@@ -1826,6 +1826,11 @@
"require-from-string" "^2.0.2"
"uri-js" "^4.2.2"
"animejs@^3.2.1":
"integrity" "sha512-sWno3ugFryK5nhiDm/2BKeFCpZv7vzerWUcUPyAZLDhMek3+S/p418ldZJbJXo5ZUOpfm2kP2XRO4NJcULMy9A=="
"resolved" "https://registry.npmjs.org/animejs/-/animejs-3.2.1.tgz"
"version" "3.2.1"
"ansi-escapes@^4.2.1", "ansi-escapes@^4.3.0":
"integrity" "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="
"resolved" "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz"
......@@ -3443,6 +3448,16 @@
"resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz"
"version" "4.2.10"
"gsap@^2.0.2":
"integrity" "sha512-8RFASCqi2FOCBuv7X4o7M6bLdy+1hbR0azg+MG7zz+EVsI+OmJblYsTk0GEepQd2Jg/ItMPiVTibF7r3EVxjZQ=="
"resolved" "https://registry.npmjs.org/gsap/-/gsap-2.1.3.tgz"
"version" "2.1.3"
"gsap@^3.11.3":
"integrity" "sha512-xc/iIJy+LWiMbRa4IdMtdnnKa/7PXEK6NNzV71gdOYUVeTZN7UWnLU0fB7Hi1iwiz4ZZoYkBZPPYGg+2+zzFHA=="
"resolved" "https://registry.npmjs.org/gsap/-/gsap-3.11.3.tgz"
"version" "3.11.3"
"gzip-size@^6.0.0":
"integrity" "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q=="
"resolved" "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz"
......@@ -3499,6 +3514,14 @@
"resolved" "https://registry.npmjs.org/he/-/he-1.2.0.tgz"
"version" "1.2.0"
"hover-effect@^1.1.0":
"integrity" "sha512-172ezjW88PYOHEerHzQWmkMXaAx4hOerE+BQ3iAN+n3Xza+TczuJKgkYlCaHhcGFUdG7is/1UcepCJECNeFscg=="
"resolved" "https://registry.npmjs.org/hover-effect/-/hover-effect-1.1.0.tgz"
"version" "1.1.0"
dependencies:
"gsap" "^2.0.2"
"three" "^0.99.0"
"hpack.js@^2.1.6":
"integrity" "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ=="
"resolved" "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz"
......@@ -5675,6 +5698,16 @@
"resolved" "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
"version" "0.2.0"
"three@^0.147.0":
"integrity" "sha512-LPTOslYQXFkmvceQjFTNnVVli2LaVF6C99Pv34fJypp8NbQLbTlu3KinZ0zURghS5zEehK+VQyvWuPZ/Sm8fzw=="
"resolved" "https://registry.npmjs.org/three/-/three-0.147.0.tgz"
"version" "0.147.0"
"three@^0.99.0":
"integrity" "sha512-DmNNq6H6nRGaqxScJ8x7v5VjdtDZR72oTVwDdKbB2BYNFxCkAoo9vdFAznEsMu9YzTV2yFvbVs7qHRzvJZzTIg=="
"resolved" "https://registry.npmjs.org/three/-/three-0.99.0.tgz"
"version" "0.99.0"
"through@^2.3.6":
"integrity" "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
"resolved" "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment