使用CSS3实现任意图片获取点,实现移入散开,移出聚集的动画特效以及悬浮动画

一、 图片获取点,实现点击散开后聚集的动画特效

(1)代码

<template>
  <div class="logo-background">
    <canvas id="canvas" v-if="loading" />
    <div class="sideBox" ref="sideBox" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
      <point :data="item" v-for="(item, index) in point" :key="index" />
    </div>
  </div>
</template>
<script>
import point from "./point";
export default {
  data() {
    return {
      //用于显示隐藏canvas
      loading: true,
      point: [],
      //聚集状态的动画数据
      originAnimation: {
        x: 0,
        y: 0,
        opacity: 1,
        scale: 1
      },
      //图片src
      src: require("../assets/logoFill.svg")
    };
  },
  components: {
    point
  },
  created() {},
  mounted() {
    this.createPointData();
  },
  methods: {
    //散开点
    handleMouseEnter() {
      const rect = document.documentElement.getBoundingClientRect();
      const sideRect = this.$refs.sideBox.getBoundingClientRect();
      const sideTop = sideRect.top - rect.top;
      const sideLeft = sideRect.left - rect.left;
      this.point = this.point.map(item => ({
        ...item,
        animation: {
          x: Math.random() * rect.width - sideLeft - item.x,
          y: Math.random() * rect.height - sideTop - item.y,
          opacity: Math.random() * 0.4 + 0.1,
          scale: Math.random() * 2.4 + 0.1
        }
      }));
    },
    //聚集点
    handleMouseLeave() {
      this.point = this.point.map(item => ({
        ...item,
        animation: this.originAnimation
      }));
    },
    //获取图像数据点
    createPointData() {
      const w = 200;
      const h = 150;
      const canvas = document.getElementById("canvas");
      const ctx = canvas.getContext("2d");
      ctx.clearRect(0, 0, w, h);
      canvas.width = w;
      canvas.height = h;
      const img = new Image();
      img.onload = () => {
        ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, w, h);
        const data = ctx.getImageData(0, 0, w, h).data;
        this.setDataToDom(data, w, h);
      };
      img.crossOrigin = "anonymous";
      img.src = this.src;
    },
    //生成具体点数据
    setDataToDom(data, w, h) {
      this.pointArray = [];
      const number = 12;
      for (let i = 0; i < w; i += number) {
        for (let j = 0; j < h; j += number) {
          if (data[(i + j * w) * 4 + 3] > 150) {
            this.pointArray.push({ x: i, y: j });
          }
        }
      }
      this.pointArray.forEach((item, i) => {
        const r = Math.random() * 10 + 6;
        const b = Math.random() * 0.4 + 0.1;
        this.point.push({
          x: item.x,
          y: item.y,
          r:r,
          b: b,
          animation: {
            x: 0,
            y: 0,
            opacity: 1,
            scale: 1
          }
        });
      });
      this.loading = false;
    }
  }
};
</script>
<style>
.logo-background {
  width: 100%;
  height: 100%;
  /* overflow: hidden; */
}
.sideBox {
  position: relative;
  width: 300px;
  height: 300px;
}
</style>

(2)point.vue

<template>
  <div
    :class="$style['point-wrapper']"
    :style="`top:${data.y}px;left:${data.x}px;opacity:${data.animation.opacity};transform:translate(${data.animation.x}px,${data.animation.y}px) scale(${data.animation.scale});`"
  >
    <div
      :class="[
        $style.point,
        {
          [$style.pointAnimation]: this.status === 'gather',
        },
      ]"
      :style="`width:${data.r}px;height:${data.r}px;transform:translate(${floatX}px,${floatY}px)`"
    ></div>
  </div>
</template>
<script>
import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
@Component()
export default class Point extends Vue {
  @Prop({ default: () => ({}), type: Object }) data;
  @Prop({ default: 'gather', type: String }) status;

  floatX = 0;
  floatY = 0;
  timer = '';
  //聚集时显示悬浮动画,散开显示散开动画
  @Watch('status')
  handleStatusChange(status) {
    if (status === 'gather') {
      this.floatX = 0;
      this.floatY = 0;
      clearInterval(this.timer);
    } else {
      this.startDisperseAnimation();
    }
  }
  startDisperseAnimation() {
    this.timer = setInterval(() => {
      this.floatX = Math.random() * 5;
      this.floatY = Math.random() * 5;
    }, 1000);
  }
}
</script>
<style lang="less" module>
.point-wrapper {
  position: absolute;
  transition: transform 1s linear;
}
.point {
  position: relative;
  // background-color: rgb(15, 15, 15);
  background-color: var(--logo-color);
  border-radius: 50%;
  transition: transform 1s linear;
}
.pointAnimation {
  -webkit-animation: ghostUpdown 2s infinite alternate;
}
@keyframes ghostUpdown {
  from {
  }
  to {
    transform: translate(0px, -10px);
  }
}
</style>

二、原理

(1)使用canvas获取图片像素点(ctx.getImageData),然后图片取点像素为控制点的个数,以图片宽度除以像素点来决定点的个数, 默认为 20, 每行每列为15个取点。

(2)使用div绘制点,每个点外部包裹一层父级div。父级div用于实现聚散动画,点div用于实现悬浮动画。

(3)父级div聚散动画使用transform:translate(x,y)实现聚散动画,使用transition: transform 1s linear;实现动画平滑过渡。使用position定位实现点的排布,具体top和left由(1)获取计算得出。

<div
    :class="$style['point-wrapper']"
    :style="`top:${data.y}px;left:${data.x}px;opacity:${data.animation.opacity};transform:translate(${data.animation.x}px,${data.animation.y}px) scale(${data.animation.scale});`"
  >
  </div>
.point-wrapper {
  position: absolute;
  transition: transform 1s linear;
}

 

(4)点div使用annimation实现悬浮动画

.pointAnimation {
  -webkit-animation: ghostUpdown 2s infinite alternate;
}
@keyframes ghostUpdown {
  from {
  }
  to {
    transform: translate(0px, -10px);
  }
}

三、效果

(1)聚集时(一张logo)

(2)散开时(散开距离自行调整,这里为了截图方便改小了距离)

https://motion.ant.design/exhibition-cn/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值