.. vue3+gasp实现【星之卡比输入框】(稀土掘金技术社区2024年10月23日文章)_众股360

您的位置 : 首页 > 公众号 > 稀土掘金技术社区

vue3+gasp实现【星之卡比输入框】(稀土掘金技术社区2024年10月23日文章)

分享到:
作者:稀土掘金技术社区 | 更新时间:2024-10-25 03:51:48

点击关注公众号,“技术干货”及时达!前言在vue3中使用gasp去实现一个星之卡比的输入框,实现对输入框中的内容进行正则判断,根据正则结果实现不同的动画效果。本组件是根据gasp官网的案例进行修改copy的有兴趣的话可以自己去对应的官网看看...

A股板块轮动加剧,跨年大妖来袭,这几只票主力已明显介入!微信搜索关注【研讯小组】公众号(可长按复制),回复666,领取代码!

点击关注公众号,“技术干货”及时达!


前言

在vue3中使用gasp去实现一个星之卡比的输入框,实现对输入框中的内容进行正则判断,根据正则结果实现不同的动画效果。
本组件是根据gasp官网的案例进行修改copy的有兴趣的话可以自己去对应的官网看看(ps:官网的是tsx的版本的,我这改成了vue3.2的setup语法糖组件)https://codepen.io/collection/naMaNQ

效果图

动画.gif

技术栈

技术栈官网
vue3https://cn.vuejs.org/
gasphttps://gsap.com/

代码结构

image.png
一、 星之卡比外观svg的绘制

外观没啥好说的直接复制就行了

//KirbyComponent.vue

二、引入gasp插件以及配置获取节点的ref

//KirbyComponent.vueimport { ref, watch, type Ref, onMounted } from 'vue'import { gsap } from 'gsap'import { MorphSVGPlugin } from 'gsap/MorphSVGPlugin'//gasp的额外插件需要用此方法注册gsap.registerPlugin(MorphSVGPlugin)/**接收父级组件传递过来的参数用来执行对应的动画 */const props = defineProps({ swallow: Boolean, spit: Boolean})const emits = defineEmits(['animationdone'])//用于获取svg节点const bodyPathFill: Ref = ref(void 0);const bodyPathStroke: Ref = ref(void 0);const starIsVisible = ref(0);const starPosition: Ref = ref(null);const bodySwallow = ref(null) as anyconst mouth = ref(null)const mouthOpen = ref(null)const face = ref(null)const armLeft = ref(null)const armRight = ref(null)const legRight = ref(null)const mouthFull = ref(null)const body = ref(null)const envelope = ref(null)const envelopeStar = ref(null)const eyeLeft = ref(null)const eyeRight = ref(null)const eyeLeftClosed = ref(null)const eyeRightClosed = ref(null)const hat = ref(null)const star = ref(null)const mouthFrown = ref(null)const cheeks = ref(null)const bodyFill: any = ref(null)const bodyStroke: any = ref(null)

三、动画方法

所有的关于星之卡比的动画都在这里了(不包含输入框文字的动画),gasp的具体属性以及方法可以去官网看看文档,这边不做详细解释了

//KirbyComponent.vue**吸取数据动画 */function animateInhale() { const tlInhale = gsap.timeline()
const delay = 0.1 const tlMouth = gsap.timeline() tlMouth .to(mouth.value, { duration: delay, scale: 0 }) .to(mouthOpen.value, { duration: 0.5, scale: 1 })
const kirbyFace = gsap.to(face.value, { delay, duration: 0.5, rotation: 5, y: -20 })
const kirbyArmLeft = gsap.to(armLeft.value, { delay, duration: 0.7, rotation: 80 })
const kirbyArmRight = gsap.to(armRight.value, { delay, duration: 0.7, rotation: -15 })
const kirbyLegRight = gsap.to(legRight.value, { delay: delay + 1, duration: 1, rotation: -15 })
tlInhale.add(tlMouth, 0) tlInhale.add(kirbyFace, 0) tlInhale.add(kirbyArmLeft, 0) tlInhale.add(kirbyArmRight, 0) tlInhale.add(kirbyLegRight, 0) return tlInhale}/**身体吸取数据动画 */function animatePuffed() { const tlPuffed = gsap.timeline()
const tlMouth = gsap.timeline() tlMouth .to(mouthOpen.value, { duration: 0.2, scale: 0 }) .to(mouthFull.value, { duration: 0.1, scale: 1, y: { duration: 0, value: 0 } as any })
const kirbyFace = gsap.to(face.value, { duration: 0.5, rotate: 0, y: 0 })
const kirbyBody = gsap.to(body.value, { duration: 1.5, ease: 'elastic', scale: 1.1 })
const kirbyArmLeft = gsap.to(armLeft.value, { duration: 0.3, rotate: 0 })
const kirbyArmRight = gsap.to(armRight.value, { duration: 1.5, ease: 'elastic', rotation: 0, x: 10 })
const kirbyLegRight = gsap.to(legRight.value, { duration: 0.6, rotation: 0, y: 10 })
tlPuffed.add(tlMouth, 0) tlPuffed.add(kirbyFace, 0) tlPuffed.add(kirbyBody, 0) tlPuffed.add(kirbyArmLeft, 0) tlPuffed.add(kirbyArmRight, 0) tlPuffed.add(kirbyLegRight, 0) return tlPuffed}/**吞下数据动画 */function animateSwallow() { const tlSwallow = gsap.timeline() const tlEyes = gsap.timeline() const tlMouth = gsap.timeline() const dur = { duration: 0.8, ease: 'elastic' }
tlEyes .to([eyeLeft.value, eyeRight.value], { duration: 0.1, scaleY: 0, y: 20 }) .to([eyeLeftClosed.value, eyeRightClosed.value], { duration: 0.1, scaleY: 1, y: 0 })
tlMouth .to(mouthFull.value, { duration: 0.1, scaleY: 0, y: 35 }) .to(mouthFrown.value, { duration: 0.1, scaleY: 1, y: { duration: 0, value: 0 } as any, onComplete: () => { starIsVisible.value = 2 } })
const kirbyCheeks = gsap.to(cheeks.value, { ...dur, y: 40 })
const kirbyBody = gsap.to(body.value, { ...dur, scale: 1 })
const bodyStroke = gsap.to(['#bodyStroke', '#bodyFill'], { ...dur, morphSVG: bodySwallow.value })
const kirbyArmLeft = gsap.to(armLeft.value, { ...dur, rotation: 70, y: 50 })
const kirbyArmRight = gsap.to(armRight.value, { ...dur, rotation: -50, x: 0, y: 50 })
tlSwallow.add(tlEyes, 0) tlSwallow.add(tlMouth, 0) tlSwallow.add(kirbyCheeks, 0) tlSwallow.add(kirbyBody, 0) tlSwallow.add(bodyStroke, 0) tlSwallow.add(kirbyArmLeft, 0) tlSwallow.add(kirbyArmRight, 0) return tlSwallow}/**邮件出现上升动画 */function animatePowerUp() { const tlPowerUp = gsap.timeline()
const delay = 0.6 const tlEnvelope = gsap.timeline()
animateReset()
const kirbyLegRight = gsap.to(legRight.value, { delay: delay + 1, duration: 1, rotation: -15 })
const kirbyHat = gsap.to(hat.value, { onStart: () => { starPosition.value = { x: -10, y: -85 } starIsVisible.value = 6 }, startAt: { opacity: 1, scale: 0, y: 0 }, delay, duration: 0.6, ease: 'elastic', scale: 1 })
tlEnvelope .to(envelope.value, { startAt: { opacity: 1, scale: 0, y: 10 }, delay: delay + 0.3, duration: 0.6, ease: 'elastic', scale: 1 }) .to(envelopeStar.value, { startAt: { rotation: 500 }, duration: 1, rotation: 0, scale: 1 }) .to(envelope.value, { delay: 1, duration: 0.2, scaleY: 0.7 }) .to(envelope.value, { duration: 0.1, scaleY: 1, y: -5 }) .to(envelope.value, { duration: 1.5, ease: 'power2.inOut', opacity: 0, y: -110 }) .to(hat.value, { delay: 0.5, duration: 0.3, opacity: 0, scale: 0.7, y: -20 }) .to(legRight.value, { duration: 0.6, rotation: 0 })
tlPowerUp.add(kirbyLegRight, 0) tlPowerUp.add(kirbyHat, 0) tlPowerUp.add(tlEnvelope, 0) return tlPowerUp}/**重置动画 */function animateReset() { const tlEyes = gsap.timeline() const tlMouth = gsap.timeline() const dur = { delay: 0.2, duration: 1, ease: 'elastic' } tlEyes .to([eyeLeftClosed.value, eyeRightClosed.value], { delay: 0.2, duration: 0.1, scaleY: 0, y: -20 }) .to([eyeLeft.value, eyeRight.value], { duration: 0.2, scaleY: 1, y: { duration: 0.1, value: 0 } as any })
tlMouth .to(mouthFrown.value, { delay: 0.2, duration: 0.1, scaleY: 0, y: -35 }) .to(mouth.value, { duration: 0.1, scale: 1 })
gsap.to(cheeks.value, { ...dur, y: 0 })
gsap.to('#bodyStroke', { ...dur, morphSVG: bodyPathStroke.value })
gsap.to('#bodyFill', { ...dur, morphSVG: bodyPathFill.value })
gsap.to(armLeft.value, { ...dur, rotation: 0, y: 0 })
gsap.to(armRight.value, { ...dur, rotation: 0, y: 0 })
gsap.to(legRight.value, { y: 0 })}
function animateStar(el: gsap.TweenTarget, done: () => void) { const tl = gsap.timeline({ onComplete: () => { starIsVisible.value = 0 done() } })
gsap.set(el, { scale: 0, transformOrigin: 'center center' })
if (starIsVisible.value > 2) { gsap.set(el, { ...starPosition.value })
tl.to(el, { delay: 'random(0, 0.5)', duration: 'random(0.7, 1.1)', opacity: 0, rotation: 'random(360, 720)', scale: 'random(0.3, 0.6)', x: `random(${starPosition.value!.x - 60}, ${starPosition.value!.x + 60})`, y: `random(${starPosition.value!.y - 60}, ${starPosition.value!.y + 60})` }) } else { tl.to(el, { delay: 'random(0, 0.5)', duration: 0.7, rotation: 500, scale: 1, x: 'random(-120, 120)', y: 'random(-120, -50)' }).to(el, { duration: 1, ease: 'elastic', opacity: 0, scale: 2 }) }}/**吐出星星动画 */function animateSpit() { const tlSpit = gsap.timeline()
const tlMouth = gsap.timeline()
tlMouth .to(mouthFull.value, { duration: 0.05, scaleY: 0 }) .to(mouthOpen.value, { duration: 0.4, scale: 0.3 }) .to(star.value, { startAt: { opacity: 1, rotation: 0, scale: 0, transformOrigin: 'center center', x: -40, y: -20 }, delay: -0.2, duration: 1, ease: 'power2.inOut', rotation: 720, scale: 1, x: -350 }) .to(star.value, { duration: 0.2, scaleX: 0.9, transformOrigin: 'left center' }) .to(star.value, { onStart: () => { starPosition.value = { x: -350, y: -20 } starIsVisible.value = 6 }, duration: 0.2, opacity: 0, scaleX: 1, transformOrigin: 'left center' }) .to( mouthOpen.value, { duration: 0.1, scale: 0 }, '-=1' ) .to( mouth.value, { duration: 0.1, scale: 1 }, '-=0.9' )
const kirbyBody = gsap.to(body.value, { duration: 0.2, scale: 1 })
const kirbyArmRight = gsap.to(armRight.value, { duration: 0.7, x: 0 })
const kirbyLegRight = gsap.to(legRight.value, { duration: 0.6, rotation: 0, y: 0 })
tlSpit.add(tlMouth, 0) tlSpit.add(kirbyBody, 0) tlSpit.add(kirbyArmRight, 0) tlSpit.add(kirbyLegRight, 0) return tlSpit}/**添加样式方法,style中配置了就可以不执行这个方法 */function styleInject(css: string, ref?: { insertAt?: any } | undefined) { if (ref === void 0) ref = {} const insertAt = ref.insertAt
if (!css || typeof document === 'undefined') { return }
const head = document.head || document.getElementsByTagName('head')[0] const style = document.createElement('style') as any style.type = 'text/css'
if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild) } else { head.appendChild(style) } } else { head.appendChild(style) }
if (style.styleSheet) { style.styleSheet.cssText = css } else { style.appendChild(document.createTextNode(css)) }}

四、组件初始化以及对是否符合正则的对应动画执行方法

在onMounted钩子里面初始化星之卡比的外观

//KirbyComponent.vueonMounted(() => { bodyPathFill.value = MorphSVGPlugin.convertToPath(bodyFill.value) as any bodyPathStroke.value = MorphSVGPlugin.convertToPath(bodyStroke.value) as any
gsap.set(mouthOpen.value, { transformOrigin: '17px 26px', scale: 0 })
gsap.set(mouthFull.value, { transformOrigin: 'center center', scale: 0 })
gsap.set(mouthFrown.value, { transformOrigin: 'center center', scaleY: 0 })
gsap.set(eyeLeft.value, { transformOrigin: 'center 17px' })
gsap.set(eyeRight.value, { transformOrigin: 'center 17px' })
gsap.set(eyeLeftClosed.value, { transformOrigin: 'center bottom', scaleY: 0 })
gsap.set(eyeRightClosed.value, { transformOrigin: 'center bottom', scaleY: 0 })
gsap.set(face.value, { transformOrigin: '25px bottom' })
gsap.set(armLeft.value, { transformOrigin: '70px 42px' })
gsap.set(armRight.value, { transformOrigin: '-35px 44px' })
gsap.set(legRight.value, { transformOrigin: '-13px -6px' })
gsap.set(body.value, { transformOrigin: 'left bottom' })
gsap.set(bodyStroke.value, { transformOrigin: 'center bottom' })
gsap.set(bodyFill.value, { transformOrigin: 'center bottom' })
gsap.set(hat.value, { scale: 0, transformOrigin: 'center bottom' })
gsap.set(envelope.value, { scale: 0, transformOrigin: 'center bottom', y: 10 })
gsap.set(envelopeStar.value, { scale: 0, transformOrigin: 'center center' })
gsap.set(star.value, { scale: 0, transformOrigin: 'center center' })})

监听props中的值,根据值去动态执行相应的动画

//设置接收props的值const props = defineProps({ swallow: Boolean, spit: Boolean})
//动画结束向父组件传递结束信息const emits = defineEmits(['animationdone'])
//监听props中的值,去执行对应方法watch(() => props.swallow, (newValue) => { if (newValue) { aswallow() }})
watch(() => props.spit, (newValue) => { if (newValue) { aspit() }})

//正则匹配为true时的动画function aswallow(): any { if (props.swallow) { animateInhale() .then(animatePuffed as any) .then(animateSwallow) .then(animatePowerUp) .then(() => { emits('animationdone') }) }}
//正则匹配为false时的动画function aspit(): any { if (props.spit) { console.log(2) animateInhale() .then(animatePuffed) .then(animateSpit) .then(() => { emits('animationdone') }) }}

五、输入框外观

//KirbyView.vue

六、配置输入框节点以及输入框文字移动动画

//KirbyView.vueimport { ref,nextTick } from 'vue';import Kirby from '@/components/kirby/KirbyComponent.vue';// import Kirby from '../components/kirby/kirby';//引入gasp插件import { gsap } from 'gsap'//配置输入框节点const emailValue = ref('');const emailIsDisabled = ref(false);const emailText = ref('');const isValid = ref(false);const isNotValid = ref(false);//输入框判断方法function submitMail() { if (emailValue.value) { const emailRegex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; isValid.value = emailRegex.test(emailValue.value); isNotValid.value = !isValid.value; emailText.value = emailValue.value.length > 15 ? emailValue.value.slice(0, 15) + '...' : emailValue.value; emailValue.value = ''; emailIsDisabled.value = true; nextTick(animateText); } } //输入框文字吸入效果function animateText() { const tlJiggle = gsap.timeline({ delay: 1, defaults: { duration: 0.1 }});
tlJiggle .to('.input-text-letter', { y: 'random(-6, 4)' }) .to('.input-text-letter', { y: 'random(-6, 4)' }) .to('.input-text-letter', { y: 'random(-6, 4)' }) .to('.input-text-letter', { y: 'random(-6, 4)' }) .to('.input-text-letter', { y: 0, duration: 0.1 }) .to('.input-text-letter', { delay: 0, duration: 0.5, ease: 'expo.in', x: 300, stagger: { amount: 0.3, from: 'end' } });}//重置动画flagfunction reset() { emailIsDisabled.value = false; isValid.value = false; isNotValid.value = false; emailText.value = '';}

代码地址

gitee:https://gitee.com/liu_soon/vue3.3

npm install

npm run dev

运行成功之后打开:http://localhost:5173/Kirby

结语

这个主要是学习gasp动画的,了解这篇文章能够较快的入门gasp这个技术。最主要的是这个输入框好看,而且适合用在博客上。


点击关注公众号,“技术干货”及时达!


A股板块轮动加剧,跨年大妖来袭,这几只票主力已明显介入!微信搜索关注【研讯小组】公众号(可长按复制),回复666,领取代码!

本站内容转载请注明来源并提供链接,数据来自互联网,仅供参考。如发现侵权行为,请联系我们删除涉嫌侵权内容。

展开

相关文章

更多>>

反馈与咨询

关于本站 反馈中心 版权声明 网站地图

  版权投诉请发邮件到1191009458#qq.com(把#改成@),我们会尽快处理

  Copyright©2023-2024众股360(www.zgu360.com).AllReserved|备案号:湘ICP备2023009521号-3

  本站资源均收集整理于互联网,其著作权归原作者所有,如有侵犯你的版权,请来信告知,我们将及时下架删除相应资源

Copyright © 2024-2024 EYOUCMS. 易优CMS 版权所有 Powered by EyouCms