vue 3 + mo.js 实现点赞粒子特效【实战】

最终效果
请添加图片描述

为方便大家下载,开源gitee地址如下
点赞粒子效果 (gitee.com)

文章较长,建议一步一步跟着走

创建一个项目

首先我们创建一个vue3.2项目

详情请见Vue3+TypeScript 项目创建_qq_22841387的博客-CSDN博客,这里就不赘述了

这里选择默认即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ktEUlF3J-1644836418151)(assets/image-20220210184050-bz1tgz9.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T2xSOJVL-1644836418151)(assets/image-20220210184112-589b4p1.png)]

一、显示页面

1.新建页面

components文件夹下新建页面ThumbsUp.vue

单文件组件遵循 大驼峰原则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NW5dQHbo-1644836418152)(assets/image-20220210185334-yol0k3w.png)]

2.写部分显示代码

<template>
  <h1>点赞页面</h1>
</template>

<script>
export default {

}
</script>

<style>

</style>

3.导入文件

App.vue文件下导入文件

可直接写名字(Vue 3会自动导入
请添加图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CVu4ZyLD-1644836418152)(assets/image-20220210220353-ip7c6ix.png)]

4.将其他的注释掉

<template>
  <!-- <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/> -->
  <thumbs-up></thumbs-up>
</template>

<script>
// import HelloWorld from './components/HelloWorld.vue'
import ThumbsUp from './components/ThumbsUp.vue'

export default {
  name: 'App',
  components: {
    // HelloWorld,
    ThumbsUp
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>


二、下载点赞图片

1.打开阿里云矢量库

打开下方链接

iconfont-阿里巴巴矢量图标库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q3RENq8x-1644836418153)(assets/image-20220210163247-210ybxm.png)]

选择一个你喜欢的图标下载,最好是空心的

2.复制SVG代码

选择完成后,将鼠标移动至所选图标

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rvFRUFnu-1644836418153)(assets/image-20220210163436-uqb9eol.png)]

点击下载

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EBN3grvw-1644836418154)(assets/image-20220210163500-wsfe8ei.png)]

在弹出的窗口中,选择复制SVG代码,直接粘贴到目标文件ThumbsUp.vue

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bkKMIon0-1644836418154)(assets/image-20220210220602-coiujl7.png)]

3.调整样式

css建议书写顺序:

  1. 布局定位属性:display / position / float / clear / visibility / overflow
  2. 自身属性:width / height / margin / padding / border / background
  3. 文本属性:color / font / text-decoration / text-align / vertical-align / white- space / break-word
  4. 其他属性(CSS3):content / cursor / border-radius / box-shadow / text-shadow / background: linear-gradient …

示例代码:

.jdc {
  display: block;
  position: relative;
  float: left;
  width: 100px;
  height: 100px;
  margin: 0 10px;
  padding: 20px 0;
  font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
  color: #333;
  background: rgba(0,0,0,.5);
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  -o-border-radius: 10px;
  -ms-border-radius: 10px;
  border-radius: 10px;
}

①包裹整体、爱心和文字

这里我使用thums-up包裹整体,局部心形用heart包裹

<template>
  <h3>点赞页面</h3>
  <div class="thums-up">
    <div class="heart">
      <svg
        t="1644501913302"
        class="icon"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="1268"
       	width="25"
        height="20"
      >
        <path
          d="M171.712 571.648l0.352 0.32 287.904 252.8a64 64 0 0 0 82.912 1.344l296.832-244.544a215.584 215.584 0 1 0-301.824-300.576L512 316.672l-25.888-35.616a215.584 215.584 0 1 0-314.4 290.624zM32 407.584a279.584 279.584 0 0 1 480-194.944 279.584 279.584 0 0 1 480 194.944 278.144 278.144 0 0 1-113.024 224.512l-295.36 243.392a128 128 0 0 1-165.888-2.592L129.984 620.16A278.976 278.976 0 0 1 32 407.584z"
          p-id="1269"
        ></path>
      </svg>
    </div>

    <div class="thums-up-text"><span>点赞</span></div>

  </div>
</template>
②修改总体样式
.thums-up {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 24px;
    cursor: pointer;
}

③修改样式
.thums-up .heart {
    display: inline-flex;
    position: relative;
    height: 20px;
}
④修改svg样式
.thums-up .heart svg {
    stroke: #9A9DAA;
    stroke-width: 30px;
    transition: fill 0.3s , stroke 0.3s;
    fill: transparent;
}

给一个延迟的动画效果,显得没有那么突兀

⑤修改文字样式
.thums-up-text {
    margin-left: 1px;
    font-size: 13px;
    user-select: none; // 用户不可选择
}
⑥增加鼠标悬浮效果
.thums-up:hover .heart svg {
  stroke: #e05b5b;
}

当前效果

请添加图片描述

三、制作粒子扩散效果

1.安装mo.js

这里使用的是一个轻量级的图形动画库

API overview | mo.js (mojs.github.io)

npm install @mojs/core

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n84K0R2p-1644836418154)(assets/image-20220210222220-o0now88.png)]

或者使用cnpm安装也可以,会快一些

cnpm install @mojs/core

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RwY1WyIW-1644836418155)(assets/image-20220210222405-a7gd7dz.png)]

出现无法安装的问题可查看这篇文章

使用cnpm i安装依赖【Npm、Cnpm】_qq_22841387的博客-CSDN博客

2.页面引用

<script>
import mojs from '@mojs/core'
export default {
  setup(){
    new mojs.Timeline()
  }
};
</script>

3.构建特效

这里我们使用的是Burst(炸裂)特效

Burst | mo.js (mojs.github.io)

①定义ref(响应式数据)
    <div class="heart" ref="heart">
……

    const heart = ref(null);

②构建burst
let burst;

    onMounted(() => {
      burst = new mojs.Burst({
        // 爆炸范围
        radius: { 0: 50 },
        // 动画挂载父元素,默认改在到body上
        parent: heart.value,
        // 动画延时函数
        easing: mojs.easing.bezier(0.1, 1, 0.3, 1),
        // 动画延时时间
        duration: 1500,
        // 动画等待时间
        delay: 300,
        // 扩散的粒子配置
        children: {
          duration: 750,
          // 随机数范围爆炸
          radius: { 0: "rand(5,25)" },
          shape: ["circle", "rect", "polygon"],
          // 粒子可选色
          fill: [
            "#1abc9c",
            "#2ecc71",
            "#00cec9",
            "#3498db",
            "#9b59b6",
            "#fdcb6e",
            "#f1c40f",
            "#e67e22",
            "#e74c3c",
            "#e84393",
          ],
          degreeShift: "rand(-90, 90)",
          delay: "stagger(0, 40)",
        },
        // 透明度
        opacity: 0.6,
        // 生成粒子数量
        count: 10,
      });
    });


③调用动画
  <div class="thums-up" @click="thumbsUp">
……

	function thumbsUp() {
      new mojs.Timeline().add(burst).play();
    }

<template>
  <h3>点赞页面</h3>
  <div class="thums-up" @click="thumbsUp">
    <div class="heart" ref="heart">
      <svg
        t="1644501913302"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="1268"
        width="25"
        height="20"
      >
        <path
          d="M171.712 571.648l0.352 0.32 287.904 252.8a64 64 0 0 0 82.912 1.344l296.832-244.544a215.584 215.584 0 1 0-301.824-300.576L512 316.672l-25.888-35.616a215.584 215.584 0 1 0-314.4 290.624zM32 407.584a279.584 279.584 0 0 1 480-194.944 279.584 279.584 0 0 1 480 194.944 278.144 278.144 0 0 1-113.024 224.512l-295.36 243.392a128 128 0 0 1-165.888-2.592L129.984 620.16A278.976 278.976 0 0 1 32 407.584z"
          p-id="1269"
        ></path>
      </svg>
    </div>

    <div class="thums-up-text"><span>点赞</span></div>
  </div>
</template>

<script>
import mojs from "@mojs/core";
import { ref, onMounted } from "vue";
export default {
  setup() {
    const heart = ref(null);

    let burst;

    onMounted(() => {
      burst = new mojs.Burst({
        // 爆炸范围
        radius: { 0: 50 },
        // 动画挂载父元素,默认改在到body上
        parent: heart.value,
        // 动画延时函数
        easing: mojs.easing.bezier(0.1, 1, 0.3, 1),
        // 动画延时时间
        duration: 1500,
        // 动画等待时间
        delay: 300,
        // 扩散的粒子配置
        children: {
          duration: 750,
          // 随机数范围爆炸
          radius: { 0: "rand(5,25)" },
          shape: ["circle", "rect", "polygon"],
          // 粒子可选色
          fill: [
            "#1abc9c",
            "#2ecc71",
            "#00cec9",
            "#3498db",
            "#9b59b6",
            "#fdcb6e",
            "#f1c40f",
            "#e67e22",
            "#e74c3c",
            "#e84393",
          ],
          degreeShift: "rand(-90, 90)",
          delay: "stagger(0, 40)",
        },
        // 透明度
        opacity: 0.6,
        // 生成粒子数量
        count: 10,
      });
    });

    function thumbsUp() {
      new mojs.Timeline().add(burst).play();
    }

    return {
      heart,
      burst,

      thumbsUp,
    };
  },
};
</script>


<style>
.thums-up {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  height: 24px;
}
.thums-up .heart {
  display: inline-flex;
  position: relative;
  height: 20px;
}

.thums-up .heart svg {
  stroke: #9a9daa;
  stroke-width: 30px;
  transition: fill 0.3s, stroke 0.3s;
  fill: transparent;
}
.thums-up:hover .heart svg {
  stroke: #e05b5b;
}

.thums-up-text {
  margin-left: 1px;
  font-size: 13px;
  user-select: none;
}
</style>

阶段演示效果

请添加图片描述

四、制作红晕

使用mo.js中的Transit制作一个空心的圆形

1.定义ref

<svg
        ref="heart_icon"
        t="1644501913302"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="1268"
        width="25"
        height="20"
      >

……

    const heart_icon = ref(null);

2.构建aperture

    let burst , aperture;

     aperture = new mojs.Transit({
        parent: heart.value,
        duration: 750,
        type: 'circle',
        radius: { 0 : 20 },
        fill: 'transparent',
        stroke: '#E05B5B',
        strokeWidth: { 20 : 0 },
        opacity: 0.6,
        isRunless: true,
        easing: mojs.easing.bezier(0, 1, 0.5, 1)
      })

3.增加动画

function thumbsUp() {
      new mojs.Timeline().add(burst , aperture).play();
    }

<template>
  <h3>点赞页面</h3>
  <div class="thums-up" @click="thumbsUp">
    <div class="heart" ref="heart">
      <svg
        ref="heart_icon"
        t="1644501913302"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="1268"
        width="25"
        height="20"
      >
        <path
          d="M171.712 571.648l0.352 0.32 287.904 252.8a64 64 0 0 0 82.912 1.344l296.832-244.544a215.584 215.584 0 1 0-301.824-300.576L512 316.672l-25.888-35.616a215.584 215.584 0 1 0-314.4 290.624zM32 407.584a279.584 279.584 0 0 1 480-194.944 279.584 279.584 0 0 1 480 194.944 278.144 278.144 0 0 1-113.024 224.512l-295.36 243.392a128 128 0 0 1-165.888-2.592L129.984 620.16A278.976 278.976 0 0 1 32 407.584z"
          p-id="1269"
        ></path>
      </svg>
    </div>

    <div class="thums-up-text"><span>点赞</span></div>
  </div>
</template>

<script>
import mojs from "@mojs/core";
import { ref, onMounted } from "vue";
export default {
  setup() {
    const heart = ref(null);
    const heart_icon = ref(null);

    let burst , aperture;
    /**
     * burst 扩散
     * aperture 红色光圈(红晕)
     */
    onMounted(() => {
      burst = new mojs.Burst({
        // 爆炸范围
        radius: { 0: 50 },
        // 动画挂载父元素,默认改在到body上
        parent: heart.value,
        // 动画延时函数
        easing: mojs.easing.bezier(0.1, 1, 0.3, 1),
        // 动画延时时间
        duration: 1500,
        // 动画等待时间
        delay: 300,
        // 扩散的粒子配置
        children: {
          duration: 750,
          // 随机数范围爆炸
          radius: { 0: "rand(5,25)" },
          shape: ["circle", "rect", "polygon"],
          // 粒子可选色
          fill: [
            "#1abc9c",
            "#2ecc71",
            "#00cec9",
            "#3498db",
            "#9b59b6",
            "#fdcb6e",
            "#f1c40f",
            "#e67e22",
            "#e74c3c",
            "#e84393",
          ],
          degreeShift: "rand(-90, 90)",
          delay: "stagger(0, 40)",
        },
        // 透明度
        opacity: 0.6,
        // 生成粒子数量
        count: 10,
      });
      aperture = new mojs.Transit({
        parent: heart.value,
        duration: 750,
        type: 'circle',
        radius: { 0 : 20 },
        fill: 'transparent',
        stroke: '#E05B5B',
        strokeWidth: { 20 : 0 },
        opacity: 0.6,
        isRunless: true,
        easing: mojs.easing.bezier(0, 1, 0.5, 1)
      })
    });

    function thumbsUp() {
      new mojs.Timeline().add(burst , aperture).play();
    }

    return {
      heart,
      heart_icon,
      burst,
      aperture,

      thumbsUp,
    };
  },
};
</script>


<style>
.thums-up {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  height: 24px;
}
.thums-up .heart {
  display: inline-flex;
  position: relative;
  height: 20px;
}

.thums-up .heart svg {
  stroke: #9a9daa;
  stroke-width: 30px;
  transition: fill 0.3s, stroke 0.3s;
  fill: transparent;
}
.thums-up:hover .heart svg {
  stroke: #e05b5b;
}

.thums-up-text {
  margin-left: 1px;
  font-size: 13px;
  user-select: none;
}
</style>

阶段演示效果

请添加图片描述

五、制作已点赞效果

1.心形样式绑定

<svg
        ref="heart_icon"
        :style="heartStyle"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        width="20"
        height="20"
      >
        <path
          d="M533.504 268.288q33.792-41.984 71.68-75.776 32.768-27.648 74.24-50.176t86.528-19.456q63.488 5.12 105.984 30.208t67.584 63.488 34.304 87.04 6.144 99.84-17.92 97.792-36.864 87.04-48.64 74.752-53.248 61.952q-40.96 41.984-85.504 78.336t-84.992 62.464-73.728 41.472-51.712 15.36q-20.48 1.024-52.224-14.336t-69.632-41.472-79.872-61.952-82.944-75.776q-26.624-25.6-57.344-59.392t-57.856-74.24-46.592-87.552-21.504-100.352 11.264-99.84 39.936-83.456 65.536-61.952 88.064-35.328q24.576-5.12 49.152-1.536t48.128 12.288 45.056 22.016 40.96 27.648q45.056 33.792 86.016 80.896z"
          p-id="2432"
        ></path>
      </svg>
……

// 是否已点赞
    const hearted = ref(false)
    const heartBounce = ref(1)
    const heartStyle = computed(() => {
      return {
        fill: `${hearted.value ? '#E05B5B' : ''}`,
        stroke: `${hearted.value ? 'E05B5B' : ''}`,
        transform: `scale3d(${heartBounce.value},${heartBounce.value},1)`,
      }
    })

2.炸裂效果点赞

burst = new mojs.Burst({
        // 爆炸范围
        radius: { 0: 50 },
      ……
        onStart(){
          hearted.value = true
        }
      });

<template>
  <h3>点赞页面</h3>
  <div class="thums-up" @click="thumbsUp">
    <div class="heart" ref="heart">
      <!-- <svg
        ref="heart_icon"
        :style="heartStyle"
        t="1644501913302"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="1268"
        width="25"
        height="20"
      >
        <path
          d="M171.712 571.648l0.352 0.32 287.904 252.8a64 64 0 0 0 82.912 1.344l296.832-244.544a215.584 215.584 0 1 0-301.824-300.576L512 316.672l-25.888-35.616a215.584 215.584 0 1 0-314.4 290.624zM32 407.584a279.584 279.584 0 0 1 480-194.944 279.584 279.584 0 0 1 480 194.944 278.144 278.144 0 0 1-113.024 224.512l-295.36 243.392a128 128 0 0 1-165.888-2.592L129.984 620.16A278.976 278.976 0 0 1 32 407.584z"
          p-id="1269"
        ></path>
      </svg> -->
      <svg
        ref="heart_icon"
        :style="heartStyle"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        width="20"
        height="20"
      >
        <path
          d="M533.504 268.288q33.792-41.984 71.68-75.776 32.768-27.648 74.24-50.176t86.528-19.456q63.488 5.12 105.984 30.208t67.584 63.488 34.304 87.04 6.144 99.84-17.92 97.792-36.864 87.04-48.64 74.752-53.248 61.952q-40.96 41.984-85.504 78.336t-84.992 62.464-73.728 41.472-51.712 15.36q-20.48 1.024-52.224-14.336t-69.632-41.472-79.872-61.952-82.944-75.776q-26.624-25.6-57.344-59.392t-57.856-74.24-46.592-87.552-21.504-100.352 11.264-99.84 39.936-83.456 65.536-61.952 88.064-35.328q24.576-5.12 49.152-1.536t48.128 12.288 45.056 22.016 40.96 27.648q45.056 33.792 86.016 80.896z"
          p-id="2432"
        ></path>
      </svg>
    </div>

    <div class="thums-up-text"><span>点赞</span></div>
  </div>
</template>

<script>
import mojs from "@mojs/core";
import { ref, onMounted, computed } from "vue";
export default {
  setup() {
    const heart = ref(null);
    const heart_icon = ref(null);

    // 是否已点赞
    const hearted = ref(false);
    const heartBounce = ref(1);
    const heartStyle = computed(() => {
      return {
        fill: `${hearted.value ? "#E05B5B" : ""}`,
        stroke: `${hearted.value ? "E05B5B" : ""}`,
        transform: `scale3d(${heartBounce.value},${heartBounce.value},1)`,
      };
    });

    let burst, aperture;
    /**
     * burst 扩散
     * aperture 红色光圈(红晕)
     */
    onMounted(() => {
      burst = new mojs.Burst({
        // 爆炸范围
        radius: { 0: 50 },
        // 动画挂载父元素,默认改在到body上
        parent: heart.value,
        // 动画延时函数
        easing: mojs.easing.bezier(0.1, 1, 0.3, 1),
        // 动画延时时间
        duration: 1500,
        // 动画等待时间
        delay: 300,
        // 扩散的粒子配置
        children: {
          duration: 750,
          // 随机数范围爆炸
          radius: { 0: "rand(5,25)" },
          shape: ["circle", "rect", "polygon"],
          // 粒子可选色
          fill: [
            "#1abc9c",
            "#2ecc71",
            "#00cec9",
            "#3498db",
            "#9b59b6",
            "#fdcb6e",
            "#f1c40f",
            "#e67e22",
            "#e74c3c",
            "#e84393",
          ],
          degreeShift: "rand(-90, 90)",
          delay: "stagger(0, 40)",
        },
        // 透明度
        opacity: 0.6,
        // 生成粒子数量
        count: 10,
        onStart() {
          hearted.value = true;
        },
      });
      aperture = new mojs.Transit({
        parent: heart.value,
        duration: 750,
        type: "circle",
        radius: { 0: 20 },
        fill: "transparent",
        stroke: "#E05B5B",
        strokeWidth: { 20: 0 },
        opacity: 0.6,
        isRunless: true,
        easing: mojs.easing.bezier(0, 1, 0.5, 1),
      });
    });

    function thumbsUp() {
      new mojs.Timeline().add(burst, aperture).play();
    }

    return {
      heart,
      heart_icon,

      hearted,
      heartBounce,
      heartStyle,

      burst,
      aperture,

      thumbsUp,
    };
  },
};
</script>


<style>
.thums-up {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  height: 24px;
}
.thums-up .heart {
  display: inline-flex;
  position: relative;
  height: 20px;
}

.thums-up .heart svg {
  stroke: #9a9daa;
  stroke-width: 60px;
  transition: fill 0.3s, stroke 0.3s;
  fill: transparent;
}
.thums-up:hover .heart svg {
  stroke: #e05b5b;
}

.thums-up-text {
  margin-left: 1px;
  font-size: 13px;
  user-select: none;
}
</style>

阶段演示效果

请添加图片描述

六、制作心跳效果

Tween | mo.js (mojs.github.io)

1.制作跳动函数

bounce = new mojs.Tween({
        duration: 1200,
        onUpdate:(progress) => {
          if(progress > 0.3) {
            // elastic 弹性的
            heartBounce.value = mojs.easing.elastic.out(1.43 * progress - 0.43);
          }else {
            heartBounce.value = 0
          }
        }
      })

2.添加进thumbsUp函数中

function thumbsUp() {
      new mojs.Timeline().add(burst, aperture, bounce).play();
    }

<template>
  <h3>点赞页面</h3>
  <div class="thums-up" @click="thumbsUp">
    <div class="heart" ref="heart">
      <!-- <svg
        ref="heart_icon"
        :style="heartStyle"
        t="1644501913302"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="1268"
        width="25"
        height="20"
      >
        <path
          d="M171.712 571.648l0.352 0.32 287.904 252.8a64 64 0 0 0 82.912 1.344l296.832-244.544a215.584 215.584 0 1 0-301.824-300.576L512 316.672l-25.888-35.616a215.584 215.584 0 1 0-314.4 290.624zM32 407.584a279.584 279.584 0 0 1 480-194.944 279.584 279.584 0 0 1 480 194.944 278.144 278.144 0 0 1-113.024 224.512l-295.36 243.392a128 128 0 0 1-165.888-2.592L129.984 620.16A278.976 278.976 0 0 1 32 407.584z"
          p-id="1269"
        ></path>
      </svg> -->
      <svg
        ref="heart_icon"
        :style="heartStyle"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        width="20"
        height="20"
      >
        <path
          d="M533.504 268.288q33.792-41.984 71.68-75.776 32.768-27.648 74.24-50.176t86.528-19.456q63.488 5.12 105.984 30.208t67.584 63.488 34.304 87.04 6.144 99.84-17.92 97.792-36.864 87.04-48.64 74.752-53.248 61.952q-40.96 41.984-85.504 78.336t-84.992 62.464-73.728 41.472-51.712 15.36q-20.48 1.024-52.224-14.336t-69.632-41.472-79.872-61.952-82.944-75.776q-26.624-25.6-57.344-59.392t-57.856-74.24-46.592-87.552-21.504-100.352 11.264-99.84 39.936-83.456 65.536-61.952 88.064-35.328q24.576-5.12 49.152-1.536t48.128 12.288 45.056 22.016 40.96 27.648q45.056 33.792 86.016 80.896z"
          p-id="2432"
        ></path>
      </svg>
    </div>

    <div class="thums-up-text"><span>点赞</span></div>
  </div>
</template>

<script>
import mojs from "@mojs/core";
import { ref, onMounted, computed } from "vue";
export default {
  setup() {
    const heart = ref(null);
    const heart_icon = ref(null);

    // 是否已点赞
    const hearted = ref(false);
    const heartBounce = ref(1);
    const heartStyle = computed(() => {
      return {
        fill: `${hearted.value ? "#E05B5B" : ""}`,
        stroke: `${hearted.value ? "E05B5B" : ""}`,
        transform: `scale3d(${heartBounce.value},${heartBounce.value},1)`,
      };
    });

    let burst, aperture, bounce;
    /**
     * burst 扩散
     * aperture 红色光圈(红晕)
     */
    onMounted(() => {
      burst = new mojs.Burst({
        // 爆炸范围
        radius: { 0: 50 },
        // 动画挂载父元素,默认改在到body上
        parent: heart.value,
        // 动画延时函数
        easing: mojs.easing.bezier(0.1, 1, 0.3, 1),
        // 动画延时时间
        duration: 1500,
        // 动画等待时间
        delay: 300,
        // 扩散的粒子配置
        children: {
          duration: 750,
          // 随机数范围爆炸
          radius: { 0: "rand(5,25)" },
          shape: ["circle", "rect", "polygon"],
          // 粒子可选色
          fill: [
            "#1abc9c",
            "#2ecc71",
            "#00cec9",
            "#3498db",
            "#9b59b6",
            "#fdcb6e",
            "#f1c40f",
            "#e67e22",
            "#e74c3c",
            "#e84393",
          ],
          degreeShift: "rand(-90, 90)",
          delay: "stagger(0, 40)",
        },
        // 透明度
        opacity: 0.6,
        // 生成粒子数量
        count: 10,
        onStart() {
          hearted.value = true;
        },
      });
      aperture = new mojs.Transit({
        parent: heart.value,
        duration: 750,
        type: "circle",
        radius: { 0: 20 },
        fill: "transparent",
        stroke: "#E05B5B",
        strokeWidth: { 20: 0 },
        opacity: 0.6,
        isRunless: true,
        easing: mojs.easing.bezier(0, 1, 0.5, 1),
      });
      bounce = new mojs.Tween({
        duration: 1200,
        onUpdate:(progress) => {
          if(progress > 0.3) {
            // elastic 弹性的
            heartBounce.value = mojs.easing.elastic.out(1.43 * progress - 0.43);
          }else {
            heartBounce.value = 0
          }
        }
      })
    });

    function thumbsUp() {
      new mojs.Timeline().add(burst, aperture, bounce).play();
    }

    return {
      heart,
      heart_icon,

      hearted,
      heartBounce,
      heartStyle,

      burst,
      aperture,
      bounce,

      thumbsUp,
    };
  },
};
</script>


<style>
.thums-up {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  height: 24px;
}
.thums-up .heart {
  display: inline-flex;
  position: relative;
  height: 20px;
}

.thums-up .heart svg {
  stroke: #9a9daa;
  stroke-width: 60px;
  transition: fill 0.3s, stroke 0.3s;
  fill: transparent;
}
.thums-up:hover .heart svg {
  stroke: #e05b5b;
}

.thums-up-text {
  margin-left: 1px;
  font-size: 13px;
  user-select: none;
}
</style>

阶段演示效果

请添加图片描述

七、制作点赞状态锁

增加一层判断,当处在已点赞状态时,取消状态

function thumbsUp() {
      if (!hearted.value) {
        new mojs.Timeline().add(burst, aperture, bounce).play();
      } else {
        hearted.value = false
      }
    }

阶段演示效果

请添加图片描述

完整代码

<template>
  <h3>点赞页面</h3>
  <div class="thums-up" @click="thumbsUp">
    <div class="heart" ref="heart">
      <!-- <svg
        ref="heart_icon"
        :style="heartStyle"
        t="1644501913302"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="1268"
        width="25"
        height="20"
      >
        <path
          d="M171.712 571.648l0.352 0.32 287.904 252.8a64 64 0 0 0 82.912 1.344l296.832-244.544a215.584 215.584 0 1 0-301.824-300.576L512 316.672l-25.888-35.616a215.584 215.584 0 1 0-314.4 290.624zM32 407.584a279.584 279.584 0 0 1 480-194.944 279.584 279.584 0 0 1 480 194.944 278.144 278.144 0 0 1-113.024 224.512l-295.36 243.392a128 128 0 0 1-165.888-2.592L129.984 620.16A278.976 278.976 0 0 1 32 407.584z"
          p-id="1269"
        ></path>
      </svg> -->
      <svg
        :style="heartStyle"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        width="20"
        height="20"
      >
        <path
          d="M533.504 268.288q33.792-41.984 71.68-75.776 32.768-27.648 74.24-50.176t86.528-19.456q63.488 5.12 105.984 30.208t67.584 63.488 34.304 87.04 6.144 99.84-17.92 97.792-36.864 87.04-48.64 74.752-53.248 61.952q-40.96 41.984-85.504 78.336t-84.992 62.464-73.728 41.472-51.712 15.36q-20.48 1.024-52.224-14.336t-69.632-41.472-79.872-61.952-82.944-75.776q-26.624-25.6-57.344-59.392t-57.856-74.24-46.592-87.552-21.504-100.352 11.264-99.84 39.936-83.456 65.536-61.952 88.064-35.328q24.576-5.12 49.152-1.536t48.128 12.288 45.056 22.016 40.96 27.648q45.056 33.792 86.016 80.896z"
          p-id="2432"
        ></path>
      </svg>
    </div>

    <div class="thums-up-text"><span>点赞</span></div>
  </div>
</template>

<script>
import mojs from "@mojs/core";
import { ref, onMounted, computed } from "vue";
export default {
  setup() {
    const heart = ref(null);

    // 是否已点赞
    const hearted = ref(false);
    const heartBounce = ref(1);
    const heartStyle = computed(() => {
      return {
        fill: `${hearted.value ? '#E05B5B' : ''}`,
        stroke: `${hearted.value ? '#E05B5B' : ''}`,
        transform: `scale3d(${heartBounce.value},${heartBounce.value},1)`,
      };
    });

    let burst, aperture, bounce;
    /**
     * burst 扩散
     * aperture 红色光圈(红晕)
     * bounce 心跳
     */
    onMounted(() => {
      burst = new mojs.Burst({
        // 爆炸范围
        radius: { 0: 50 },
        // 动画挂载父元素,默认改在到body上
        parent: heart.value,
        // 动画延时函数
        easing: mojs.easing.bezier(0.1, 1, 0.3, 1),
        // 动画延时时间
        duration: 1500,
        // 动画等待时间
        delay: 300,
        // 扩散的粒子配置
        children: {
          duration: 750,
          // 随机数范围爆炸
          radius: { 0: "rand(5,25)" },
          shape: ["circle", "rect", "polygon"],
          // 粒子可选色
          fill: [
            "#1abc9c",
            "#2ecc71",
            "#00cec9",
            "#3498db",
            "#9b59b6",
            "#fdcb6e",
            "#f1c40f",
            "#e67e22",
            "#e74c3c",
            "#e84393",
          ],
          degreeShift: "rand(-90, 90)",
          delay: "stagger(0, 40)",
        },
        // 透明度
        opacity: 0.6,
        // 生成粒子数量
        count: 10,
        onStart() {
          hearted.value = true;
        },
      });
      aperture = new mojs.Transit({
        parent: heart.value,
        duration: 750,
        type: "circle",
        radius: { 0: 20 },
        fill: "transparent",
        stroke: "#E05B5B",
        strokeWidth: { 20: 0 },
        opacity: 0.6,
        isRunless: true,
        easing: mojs.easing.bezier(0, 1, 0.5, 1),
      });
      bounce = new mojs.Tween({
        duration: 1200,
        onUpdate: (progress) => {
          if (progress > 0.3) {
            // elastic 弹性的
            heartBounce.value = mojs.easing.elastic.out(1.43 * progress - 0.43);
          } else {
            heartBounce.value = 0;
          }
        },
      });
    });

    function thumbsUp() {
      if (!hearted.value) {
        new mojs.Timeline().add(burst, aperture, bounce).play();
      } else {
        hearted.value = false;
      }
    }

    return {
      heart,

      hearted,
      heartBounce,
      heartStyle,

      burst,
      aperture,
      bounce,

      thumbsUp,
    };
  },
};
</script>


<style>
.thums-up {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  height: 24px;
}
.thums-up .heart {
  display: inline-flex;
  position: relative;
  height: 20px;
}
.thums-up .heart svg {
  stroke: #9a9daa;
  stroke-width: 60px;
  transition: fill 0.3s, stroke 0.3s;
  fill: transparent;
}
.thums-up:hover .heart svg {
  stroke: #e05b5b;
}

.thums-up-text {
  margin-left: 1px;
  font-size: 13px;
  user-select: none;
}
</style>

最终效果

请添加图片描述

思考与总结

终于敲完了,内心也十分喜悦,终于独立完成了一个小小的项目。这个项目中用到了mo.jsvue3的一些钩子函数(例如,onMountedonComputed等等),特别是ref响应式数据,目前感觉这个点挺神奇(reactive

  • 这个项目的所有属性和方法都是写在setup(即vue2中的createdbeforeCreated)中的,按道理来说是不需要的,这个小项目也算是带我领略了一番vue3的风采吧。

  • 知道了css样式编写大致格式(最好统一,见名知意)

  • 还有就是第一次尝试跟着博客做一个开源的项目,尽管很小,很简单,但是也给了我一些开源项目的方法

    • 1、将代码复制到本地跑起来

    • 2、另起炉灶,跟着博客大致搭建起来(在抄写代码的时候,改成自己的代码风格,比如在这个项目中,我用到了css代码风格)

    • 3、遇见效果不一致的先自己猜测可能出现问题的地方,对比差别,逐一排查(一个一个替换进去,再思考为什么)

      其实,这个项目中有一些css样式,我就没有抄写

      因为我发现效果是一样的,所以就没有添加,比如说imargin-right


参考博客:

制作粒子扩散的点赞动效果 - 掘金 (juejin.cn)

  • 16
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值