anime 动态线

78 篇文章 2 订阅
52 篇文章 1 订阅

在这里插入图片描述

app.vue

<template>
    <div class="View" ref="View">
        <div class="Layout" :style="Layout">
            <Block v-for="item in blockData" :data="item" />
        </div>
        <svg class="Layout" :style="Layout">
            <Line v-for="item in LinkData" :data="item" :count="XCounts" />
        </svg>
    </div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import Block from './Block.vue';
import Line from './Line.vue'

const View = ref();
// 随机函数

const Layout = ref({});
const random = (min, max) => min + Math.floor(Math.random() * (max - min + 1));

const blockData = ref([]);
const LinkData = ref([]);
const XCounts = ref(0);

// 生成测试数据
onMounted(() => {
    var XLength = parseInt(View.value.clientWidth / 100);
    var Ylength = parseInt(View.value.clientHeight / 100);
    // console.log(XLength);
    XCounts.value = XLength;

    var AllCounts = XLength * Ylength;

    Layout.value.width = XLength * 100 + 'px';
    Layout.value.height = Ylength * 100 + 'px';
    Layout.value.left = (View.value.clientWidth - XLength * 100) / 2 + 'px';
    Layout.value.top = (View.value.clientHeight - Ylength * 100) / 2 + 'px';


    // 生成测试数据
    for (let i = 0; i < AllCounts; i++) {
        var Data = {
            isLink: Math.random() < 0.333,
            i: i,
        };
        blockData.value.push(Data);
        if (Data.isLink) {
            LinkData.value.push(Data);
        }
    };
    var ActiveIndex = random(0, LinkData.value.length - 1)
    for (let i = 0; i < LinkData.value.length; i++) {
        const element = LinkData.value[i];
        element.linkTo = LinkData.value[ActiveIndex].i;
    }

})


</script>
<style lang="less" scoped>
.View {
    width: 100%;
    height: 100%;
    position: relative;
}

.Layout {
    position: absolute;
}

svg.Layout {
    pointer-events: none;
}
</style>

Block.vue

<template>
    <div class="Block" :class="{ 'HasAni': data.isLink }" @mouseenter="Play">
        <environment-outlined v-if="data.isLink" class="Icon" ref="icon" />
    </div>
</template>
<script setup>
import { EnvironmentOutlined } from '@ant-design/icons-vue'
import anime from 'animejs'
import { onMounted, onUnmounted, ref } from 'vue';
const props = defineProps({
    data: Object
})
const icon = ref();
let AniObj = null;

onMounted(() => {
    // 创建一个动画(其实也可以中关键帧实现,原理是一样的)
    AniObj = anime({
        targets: icon.value,
        keyframes: [
            { rotateY: 0 },
            { translateY: -15, rotateY: 180 },
            { translateY: 0, rotateY: 360 },
        ],
        easing: 'linear',
        // 持续时间
        duration: 1000,
        autoplay: false,
    })
})

// 鼠标进入时,中心播放动画
// 用 CSS 实现时,就是删除动画的 Class,间隔 1毫秒, 再添加这个Class就可以了
const Play = () => {
    AniObj.restart();
}

onUnmounted(() => {
    // 目前 Anime 没有释放缓存的办法,
    // 只能停止动画
    AniObj.pause();
})
</script>
<style lang="less" scoped>
.Block {
    width: 100px;
    height: 100px;
    float: left;
    background-color: rgba(127, 255, 212, 0.1);
    text-align: center;
    line-height: 85px;
    position: relative;
}

.HasAni {
    transition: box-shadow 0.3s, background-color 0.2s;
}

.HasAni:hover {
    box-shadow: inset 0 0 10px rgba(0, 255, 255, 0.3);
    background-color: rgba(127, 255, 212, 0.2);
}

.Icon {
    position: relative;
    z-index: 2;
    font-size: 25px;
    color: rgb(27, 141, 216);
    transform: translateY(0px) rotateY(0deg);
}
</style>

Line.vue

<template>
    <path ref="path" :style="{ 'animation-delay': delayTime + 's' }"
        :d="`M${Start[0]} ${Start[1]} S${Control[0]} ${Control[1]} ${End[0]} ${End[1]}`" />

</template>
<script setup>
import { ref } from 'vue';
import { computePosition, ControlPointCompute } from './Uint.js'
const props = defineProps({
    data: Object,
    count: Number
})
var delayTime = Math.random();
var { Form, To } = computePosition(props.data.linkTo, props.data.i, props.count);
const Start = ref(Form);
const End = ref(To);
const Control = ref(ControlPointCompute(Form, To));

</script>
<style lang="less" scoped>
path {
    stroke: rgba(255, 0, 0, 0.5);
    stroke-width: 3;
    fill: none;
    stroke-width: 4;
    stroke-dasharray: 500, 500;
    animation: dash linear infinite 3s;
}

@keyframes dash {
    to {
        stroke-dashoffset: -1000;
    }
}
</style>

Uint.js


// 格式化传入点的信息
export var computePosition = (Form, to, count) => {
    return {
        Form: [Form % count * 100 + 50, parseInt(Form / count) * 100 + 50],
        To: [to % count * 100 + 50, parseInt(to / count) * 100 + 50],
    }
}

// 弧度值转角度值的常量
const R2A = 180 / Math.PI;
// 角度值转弧度值的常量
const A2R = (2 * Math.PI) / 360

const ConstantAngle = 45;

export var ControlPointCompute = (X, Y) => {
    // X轴坐标点差值
    const DX = Y[0] - X[0];
    // Y轴坐标点差值
    const DY = Y[1] - X[1];
    // 鼠标位置与原点的弧度值
    const radian = Math.atan2(DY, DX);
    // 鼠标点与坐标点的 余弦 值
    const CosRadian = Math.cos(radian);
    // 计算控制点与原点的距离
    const dis = Math.sqrt(Math.pow(Math.abs(DX), 2) + Math.pow(Math.abs(DY), 2)) / (2 - Math.abs(CosRadian) * (1 - 0.414));
    // 计算控制点的弧度值
    const ControlRadian = A2R * (radian * R2A - (CosRadian * ConstantAngle));
    // 计算控制点位置
    return [Math.cos(ControlRadian) * dis + X[0], Math.sin(ControlRadian) * dis + X[1]];
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值