首页 > 其他分享 >线条流动动画

线条流动动画

时间:2023-06-17 10:11:46浏览次数:43  
标签:动画 canvas target 流动 number segm length 线条 Math

简介

流线动画效果,适合做网页背景

效果展示

ts代码

注意:动画定时刷新的机制使用到了之前写的一篇文章《Vue3中循环任务优化方案》

import { useSchedule } from "@/use/sys/useSchedule";
import { Ref } from "vue";

class segm {
  b: number;
  x0: number;
  y0: number;
  a: number;
  x1: number;
  y1: number;
  l: number;

  constructor(x: number, y: number, l: number) {
    this.b = Math.random() * 1.9 + 0.1
    this.x0 = x
    this.y0 = y
    this.a = Math.random() * 2 * Math.PI
    this.x1 = this.x0 + l * Math.cos(this.a)
    this.y1 = this.y0 + l * Math.sin(this.a)
    this.l = l
  }

  update(x: number, y: number) {
    this.x0 = x
    this.y0 = y
    this.a = Math.atan2(this.y1 - this.y0, this.x1 - this.x0)
    this.x1 = this.x0 + this.l * Math.cos(this.a)
    this.y1 = this.y0 + this.l * Math.sin(this.a)
  }
}

class rope {

  c: CanvasRenderingContext2D;
  color: Ref<string>;
  res: number;
  type: string;
  l: number;
  segm: segm[];
  b: number;

  constructor(tx: number, ty: number, l: number, b: number, slq: number, typ: string, c: CanvasRenderingContext2D, color: Ref<string>) {
    this.color = color;
    this.c = c;
    if (typ == "l") {
      this.res = l / 2
    }
    else {
      this.res = l / slq
    }
    this.type = typ
    this.l = l
    this.segm = []
    this.segm.push(new segm(tx, ty, this.l / this.res))
    for (let i = 1; i < this.res; i++) {
      this.segm.push(
          new segm(this.segm[i - 1].x1, this.segm[i - 1].y1, this.l / this.res)
      )
    }
    this.b = b
  }

  update(t: { x: number, y: number }) {
    this.segm[0].update(t.x, t.y)
    for (let i = 1; i < this.res; i++) {
      this.segm[i].update(this.segm[i - 1].x1, this.segm[i - 1].y1)
    }
  }

  show() {
    if (this.type == "l") {
      this.c.beginPath()
      for (let i = 0; i < this.segm.length; i++) {
        this.c.lineTo(this.segm[i].x0, this.segm[i].y0)
      }
      this.c.lineTo(
          this.segm[this.segm.length - 1].x1,
          this.segm[this.segm.length - 1].y1
      )
      this.c.strokeStyle = this.color.value;
      this.c.lineWidth = this.b
      this.c.stroke()

      this.c.beginPath()
      this.c.arc(this.segm[0].x0, this.segm[0].y0, 1, 0, 2 * Math.PI)
      this.c.fillStyle = this.color.value;
      this.c.fill()

      this.c.beginPath()
      this.c.arc(
          this.segm[this.segm.length - 1].x1,
          this.segm[this.segm.length - 1].y1,
          2,
          0,
          2 * Math.PI
      )
      this.c.fillStyle = this.color.value;
      this.c.fill()
    }
    else {
      for (let i = 0; i < this.segm.length; i++) {
        this.c.beginPath()
        this.c.arc(this.segm[i].x0, this.segm[i].y0, this.segm[i].b, 0, 2 * Math.PI)
        this.c.fillStyle = this.color.value;
        this.c.fill()
      }
      this.c.beginPath()
      this.c.arc(
          this.segm[this.segm.length - 1].x1,
          this.segm[this.segm.length - 1].y1,
          2, 0, 2 * Math.PI
      )
      this.c.fillStyle = this.color.value;
      this.c.fill()
    }
  }
}

const schedule = useSchedule().schedule;

/**
 * 线段流动动画
 */
export class RopeFlow {

  canvas: HTMLCanvasElement;

  c: CanvasRenderingContext2D;

  id: string;

  h: number;
  w: number;

  ropes: rope[] = [];

  randl: number[] = [];

  da: number[] = [];

  target: { x: number, y: number, errx: number, erry: number } = {x: 0, y: 0, errx: 0, erry: 0};

  rl: number = 50;

  t: number = 0;

  q: number = 10;

  constructor(canvas: HTMLCanvasElement, id: string, color: Ref<string>) {
    this.canvas = canvas;
    this.id = id;
    const c = canvas.getContext("2d") as CanvasRenderingContext2D,
        w = (canvas.width = window.innerWidth),
        h = (canvas.height = window.innerHeight);
    c.fillStyle = "rgba(30,30,30,1)";
    c.fillRect(0, 0, w, h);
    this.c = c;
    this.w = (canvas.width = window.innerWidth - 10)
    this.h = (canvas.height = window.innerHeight - 10)
    let type = "l";
    for (let i = 0; i < 100; i++) {
      type = Math.random() > 0.25 ? 'l' : 'o';
      this.ropes.push(
          new rope(
              w / 2,
              h / 2,
              (Math.random() + 0.5) * 500,
              Math.random() * 0.4 + 0.1,
              Math.random() * 15 + 5,
              type,
              c,
              color
          )
      )
      this.randl.push(Math.random() * 2 - 1)
      this.da.push(0)
    }
    this.target.x = this.w / 2;
    this.target.y = this.h / 2;
  }

  run() {
    schedule.setLoopTask(
        this.id,
        () => {
          window.requestAnimationFrame(() => {
            this.loop();
          })
        },
        1000 / 40
    )
    window.addEventListener("resize", () => {
      this.w = this.canvas.width = window.innerWidth;
      this.h = this.canvas.height = window.innerHeight;
      this.loop();
    })
  }

  stop() {
    schedule.removeTask(this.id)
  }

  private loop() {
    this.c.clearRect(0, 0, this.w, this.h)
    this.draw()
  }

  private draw() {
    this.target.errx =
        this.w / 2 +
        (this.h / 2 - this.q) *
        Math.sqrt(2) *
        Math.cos(this.t) /
        (Math.pow(Math.sin(this.t), 2) + 1) -
        this.target.x
    this.target.erry =
        this.h / 2 +
        (this.h / 2 - this.q) *
        Math.sqrt(2) *
        Math.cos(this.t) *
        Math.sin(this.t) /
        (Math.pow(Math.sin(this.t), 2) + 1) -
        this.target.y
    this.target.x += this.target.errx / 10
    this.target.y += this.target.erry / 10

    this.t += 0.01

    for (let i = 0; i < this.ropes.length; i++) {
      if (this.randl[i] > 0) {
        this.da[i] += (1 - this.randl[i]) / 10
      }
      else {
        this.da[i] += (-1 - this.randl[i]) / 10
      }
      this.ropes[i].update({
        x:
            this.target.x +
            this.randl[i] * this.rl * Math.cos((i * 2 * Math.PI) / this.ropes.length + this.da[i]),
        y:
            this.target.y +
            this.randl[i] * this.rl * Math.sin((i * 2 * Math.PI) / this.ropes.length + this.da[i])
      })
      this.ropes[i].show()
    }
  }
}

使用示例

<!-- 定义一个canvas用来绘画 -->
<canvas id="login-background-animation" class="absolute w-full h-full"/>

....somecode

<script lang="ts" setup>
import { useThemeStore } from "@/stores/theme";
import { computed, onMounted, onUnmounted, ref } from "vue";
import { RopeFlow } from "@/util/effects/animationUtil";

let animationInstance: RopeFlow;

onMounted(() => {
  const canvas = document.getElementById("login-background-animation") as HTMLCanvasElement;
  if (canvas) {
    const ropeColor = computed(() => {
      return useThemeStore().colorScheme === 'light' ? "#bbddff" : '#fff7ed';
    })
    // 设置动画
    animationInstance = new RopeFlow(
        canvas,
        "Login-Background-Rope-Flow-Animation",
        ropeColor
    )
    // 运行动画
    animationInstance.run();
  }
})
onUnmounted(() => {
  if (animationInstance) {
    animationInstance.stop();
  }
})
</script>

标签:动画,canvas,target,流动,number,segm,length,线条,Math
From: https://www.cnblogs.com/Scarlett-K/p/17476960.html

相关文章

  • 3d翻转动画 vue3 ts
    <template><section><divclass="flip-container"><divclass="cards":class="{flipped:isFlipped}"><divclass="front"></div><......
  • 遮罩动画
     遮罩动画(1) 在Flash的作品中,我们常常看到很多眩目神奇的效果,而其中不少就是用最简单的“遮罩”完成的,如水波、万花筒、百页窗、放大镜、望远镜……等等。 那么,“遮罩”如何能产生这些效果呢?在本节,我们除了给大家介绍“遮罩”的基本知识,还结合我们的实际经验介绍一些“遮罩”的......
  • 动作补间动画
    动作补间动画(1) 动作补间动画也是Flash 运用动作补间动画,你可以设置元件的大小、位置、颜色、透明度、旋转等种种属性,配合别的手法,你甚至能做出令人称奇的仿3D 1(1)动作补间动画的概念 在一个关键帧上放置一个元件,然后在另一个关键帧改变这个元件的大小、颜色、位置、透......
  • 逐帧动画与人运动动画制作
    人走路动画制作 人的走路动作:    因此我们只要画出5个连续的动作,便能完成一个走路的动画,如下例一   注意在走路时候的平移,确保脚落地时候没有发生位移,同时需要控制每个动作的时间,然速度看起来很自然。 上面是以5个帧完成一次步行的例子,如果我们想要让走路更加流畅,那么就必......
  • 形状补间动画
    形状补间动画(1) 形状补间动画是Flash中非常重要的表现手法之一,运用它,你可以变幻出各种奇妙的、不可思议的变形效果。本节从形状补间动画基本概念入手,带你认识形状补间动画在时间帧上的表现,了解补间动画的创建方法,学会应用“形状提示”让图形的形变自然流畅,最后,提供了二个实例,帮......
  • vue3 css ts 双重弹跳加载动画
    /双重弹跳加载动画*/效果如同页面https://codepen.io/yjx123/pen/zYMvbML<ahref="javascript:void(0)"@click="startLoading"><inline-svg:src="getAssetPath(iconPath)"></inline-svg><div:style="{......
  • CSS动画和过渡的区别
    CSS动画和过渡都是用来实现网页元素的动态效果,但它们在实现方式和应用场景上有一些区别。CSS过渡(CSSTransitions)是在元素状态改变时实现平滑过渡效果的一种机制。过渡通过指定元素的属性变化,使元素从一个状态平滑地过渡到另一个状态。过渡通常用于响应用户操作、交互效果或状态......
  • 使用NSTimer和CGAffineTransformMakeRotation实现旋转动画
     使用NSTimer和CGAffineTransformMakeRotation实现旋转动画 首先定义需要用到的变量   floatangle;   NSTimer*timer; #pragmamark------------------->旋转图片<--------------------(void)_doRotateImage{//演员初始化UIImageView*ivImage=[[UII......
  • 【原】iOSCoreAnimation动画系列教程(一):CABasicAnimation【包会】
    【原】iOSCoreAnimation动画系列教程(一):CABasicAnimation【包会】 在iOS中,图形可分为以下几个层次: 越上层,封装程度越高,动画实现越简洁越简单,但是自由度越低;反之亦然。本文着重介绍CoreAnimation层的基本动画实现方案。在iOS中,展示动画可以类比于显示生活中的“拍电影”。拍电影有......
  • css实现向上滚动动画
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"/><metahttp-equiv="X-UA-Compatible"content="IE=edge"/><metaname="viewport"content="w......