canvas随机线条鼠标跟随背景代码分享【JS】

在这里插入图片描述

背景

第一次看见这个效果,很惊艳,毕竟当时自己才算是刚入编程这个领域,也没有参加工作,对于编程了解的甚少,对于这种偏向于计算的东西更是了解甚少,对于当前而言,让我自己去讲数学知识与编程联系起来,也是一大难事,一点不参考的写出来,感觉也是个比较困难的事情。

今天能够分享出来,更多的也是慰藉一下,像当年我一样的人,喜欢这种,但是苦于网上没有资源,找了很久,不是vip就是需要收费,想去了解,被拒之于门外。

源码

将之封装为了vue组件还有js文件。有缘者自取。

JS源码

// 根据标签名称获取dom元素
function getElementFn(param) {
  return document.getElementsByTagName(param)
}
// 返回指定属性名的属性值 (valD:默认值)
// 通过元素节点的属性名称获取属性的值
function getElementAttrFn(dom, key, valD) {
  if (key == 'color') {
    return valD
  }
  return dom.getAttribute(key) || valD
}

function getOptionsFn() {
  var domData = getElementFn('script')
  var dataLength = domData.length
  var scriptDom = domData[dataLength - 1]
  return {
    tagLength: dataLength,
    zIndex: getElementAttrFn(scriptDom, 'zIndex', -1), //层级
    opacity: getElementAttrFn(scriptDom, 'opacity', 1), //透明度
    color: getElementAttrFn(scriptDom, 'color', `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`), //颜色///没有感觉管用
    count: getElementAttrFn(scriptDom, 'count', 100) //线条数量
  }
}

// 获取canvas的宽高同时为canvas设置宽高
function getWidthOrHeight() {
  ;(canvasWidth = canvas.width =
    window.innerWidth ||
    document.documentElement.clientWidth ||
    document.body.clientWidth),
    (canvasHeight = canvas.height =
      window.innerHeight ||
      document.documentElement.clientHeight ||
      document.body.clientHeight)
}

//  获取requestAnimationFrame方法
// 浏览器可以优化并行的动画动作,更合理的重新排列动作序列,并把能够合并的动作放在一个渲染周期内完成,从而呈现出更流畅的动画效果
function getRequestAnimationFrameFn() {
  return (
    window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    ((callBack) => {
      window.setTimeout(callBack, 1000 / 45)
    })
  )
}

function drawLineFn() {
  clearCanvas()
  randomPointList.forEach((item, index) => {
    item.typeAX += item.typeBX
    item.typeAY += item.typeBY
    item.typeBX *= item.typeAX > canvasWidth || item.typeAX < 0 ? -1 : 1
    item.typeBY *= item.typeAY > canvasHeight || item.typeAY < 0 ? -1 : 1
    // 线段连接点
    // contextObj.fillRect(item.typeAX - 2.5, item.typeAY - 2.5, 5, 5)
    // 线段圆形连接点绘制
    contextObj.fillStyle = styleOptions.color
    contextObj.beginPath()
    contextObj.arc(item.typeAX - 0.5, item.typeAY - 0.5, 2.5, 0, Math.PI * 2, true);
    contextObj.closePath();
    contextObj.fill();
    for (let e = index + 1; e < randomOptions.length; e++) {
      let randomOpItem = randomOptions[e]
      if (randomOpItem.typeAX !== null && randomOpItem.typeAY !== null) {
        // 两点横坐标差值
        let diffX = item.typeAX - randomOpItem.typeAX
        // 两点纵坐标差值
        let diffY = item.typeAY - randomOpItem.typeAY
        // 计算两点间线段的距离
        let pointDis = diffX * diffX + diffY * diffY
        if (pointDis < randomOpItem.max) {
          if (randomOpItem == curMouseXY && pointDis >= randomOpItem.max / 2) {
            item.typeAX -= 0.03 * diffX
            item.typeAY -= 0.03 * diffY
          }
          let lineW = ((randomOpItem.max - pointDis) / randomOpItem.max)*1.5
          contextObj.beginPath()
          // 线宽
          contextObj.lineWidth = lineW
          contextObj.strokeStyle = styleOptions.color
          contextObj.moveTo(item.typeAX, item.typeAY)
          contextObj.lineTo(randomOpItem.typeAX, randomOpItem.typeAY)
          contextObj.stroke()
        }
      }
    }
  })
  var animationFn = getRequestAnimationFrameFn()
  animationFn(drawLineFn)
}

// 清空画布
function clearCanvas() {
  contextObj.clearRect(0, 0, canvasWidth, canvasHeight)
}

// 通过随机取值得到的参数位置
var randomOptions = []
// 当前鼠标位置
var curMouseXY = { typeAX: null, typeAY: null, max: 20000 }
// 获取线条的配置参数
var styleOptions = getOptionsFn()
console.log(styleOptions)
// canvas宽度
var canvasWidth = ''
// canvas高度
var canvasHeight = ''
// 创建canvas标签
var canvas = document.createElement('canvas')
// 获取context对象
var contextObj = canvas.getContext('2d')
// 为canvas设置id
canvas.id = `canvas${styleOptions.tagLength}`
// 为canvas设置style
canvas.style.cssText = `position:fixed;top:0;left:0;z-index:${styleOptions.zIndex};opacity:${styleOptions.opacity}`
// 将canvas插入body
var bodyDom = getElementFn('body')[0]
bodyDom.appendChild(canvas)
// 获取canvas的宽高同时为canvas设置宽高
getWidthOrHeight()


// 窗户变化重新获取宽高
window.onresize = getWidthOrHeight()
// 鼠标移动事件
window.onmousemove = function (event) {
  event = event ? event : window.event
  curMouseXY.typeAX = event.clientX
  curMouseXY.typeAY = event.clientY
}
// 鼠标移出事件
window.onmouseout = function () {
  curMouseXY.typeAX = null
  curMouseXY.typeAY = null
}

// 随机组合点坐标
var randomPointList = []
for (f = 0; f < styleOptions.count; f++) {
  randomPointList.push({
    typeAX: Math.random() * canvasWidth,
    typeAY: Math.random() * canvasHeight,
    typeBX: 2 * Math.random() - 1,
    typeBY: 2 * Math.random() - 1,
    max: 40000 // 用来控制线段的长度
  })
}

randomOptions = randomPointList.concat([curMouseXY])
setTimeout(function () {
  drawLineFn()
}, 100)

vue 组件


<template>
  <div class="lbg-wrapper">
    <canvas id="canvasDom" class="canvas-dom"
      >您的浏览器不支持canvas标签。</canvas
    >
  </div>
</template>

<script>
export default {
  name: "loginBg",
  data() {
    return {
      canWidth: "",
      canHeight: "",
      canvasDom: "",
      contextObj: "",
      styleOptions: {},
      curMouseXY: { typeAX: null, typeAY: null, max: 40000 },
      randomPointList: [],
      randomOptions: [],
    };
  },
  mounted() {
    this.canvasDom = this.getEleByIdFn("canvasDom");
    // 获取线条的配置参数
    this.styleOptions = this.setOptionsFn();
    // 获取context对象
    this.contextObj = this.canvasDom.getContext("2d");
    // 为canvas设置id
    this.canvasDom.id = `canvas${this.styleOptions.tagLength}`;
    // 为canvas设置style
    this.canvasDom.style.cssText = `position:fixed;top:0;left:0;z-index:${this.styleOptions.zIndex};opacity:${this.styleOptions.opacity}`;
    // 获取canvas的宽高同时为canvas设置宽高
    this.getWidthOrHeight();

    // 窗户变化重新获取宽高
    window.onresize = this.getWidthOrHeight();
    // 鼠标移动事件
    window.onmousemove =  (event)=> {
      event = event ? event : window.event;
      this.curMouseXY.typeAX = event.clientX;
      this.curMouseXY.typeAY = event.clientY;
    };
    // 鼠标移出事件
    window.onmouseout =  ()=> {
      this.curMouseXY.typeAX = null;
      this.curMouseXY.typeAY = null;
    };

    // 随机组合点坐标

    for (let f = 0; f < this.styleOptions.count; f++) {
      this.randomPointList.push({
        typeAX: Math.random() * this.canWidth,
        typeAY: Math.random() * this.canHeight,
        typeBX: 2 * Math.random() - 1,
        typeBY: 2 * Math.random() - 1,
        max: 20000, // 用来控制线段的长度
      });
    }

    this.randomOptions = this.randomPointList.concat([this.curMouseXY]);
    this.drawLineFn();
    // setTimeout( ()=> {
    //   this.drawLineFn();
    // }, 100);
  },
  methods: {
    // 根据ID获取dom元素
    getEleByIdFn(str) {
      return document.getElementById(str);
    },
    // 获取指定元素的属性值
    getEleAttrFn(dom, key, val) {
      if (key === "color") {
        return val;
      }
      return dom.getAttribute(key) || val;
    },
    // 设置配置项
    setOptionsFn() {
      return {
        id: 1,
        zIndex: 1, //层级
        opacity: 1, //透明度
        color: `rgb(${Math.floor(Math.random() * 255)},${Math.floor(
          Math.random() * 255
        )},${Math.floor(Math.random() * 255)})`, //颜色
        count: 100, //线条数量
      };
    },
    // 获取屏幕宽高
    getWidthOrHeight() {
      this.canWidth = this.canvasDom.width =
        window.innerWidth ||
        document.documentElement.clientWidth ||
        document.body.clientWidth;
      this.canHeight = this.canvasDom.height =
        window.innerHeight ||
        document.documentElement.clientHeight ||
        document.body.clientHeight;
    },
    //  获取requestAnimationFrame方法
    // 浏览器可以优化并行的动画动作,更合理的重新排列动作序列,并把能够合并的动作放在一个渲染周期内完成,从而呈现出更流畅的动画效果
    getRequestAnimationFrameFn() {
      return (
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        ((callBack) => {
          window.setTimeout(callBack, 1000 / 45);
        })
      );
    },
    drawLineFn() {
      this.clearCanvas();
      this.randomPointList.forEach((item, index) => {
        item.typeAX += item.typeBX;
        item.typeAY += item.typeBY;
        item.typeBX *= item.typeAX > this.canWidth || item.typeAX < 0 ? -1 : 1;
        item.typeBY *= item.typeAY > this.canHeight || item.typeAY < 0 ? -1 : 1;
        // 线段连接点
        // this.contextObj.fillRect(item.typeAX - 2.5, item.typeAY - 2.5, 5, 5)
        // 线段圆形连接点绘制
        this.contextObj.fillStyle = this.styleOptions.color;
        this.contextObj.beginPath();
        this.contextObj.arc(
          item.typeAX - 0.5,
          item.typeAY - 0.5,
          2.5,
          0,
          Math.PI * 2,
          true
        );
        this.contextObj.closePath();
        this.contextObj.fill();
        for (let e = index + 1; e < this.randomOptions.length; e++) {
          let randomOpItem = this.randomOptions[e];
          if (randomOpItem.typeAX !== null && randomOpItem.typeAY !== null) {
            // 两点横坐标差值
            let diffX = item.typeAX - randomOpItem.typeAX;
            // 两点纵坐标差值
            let diffY = item.typeAY - randomOpItem.typeAY;
            // 计算两点间线段的距离
            let pointDis = diffX * diffX + diffY * diffY;
            if (pointDis < randomOpItem.max) {
              if (
                randomOpItem == this.curMouseXY &&
                pointDis >= randomOpItem.max / 2
              ) {
                item.typeAX -= 0.03 * diffX;
                item.typeAY -= 0.03 * diffY;
              }
              let lineW =
                ((randomOpItem.max - pointDis) / randomOpItem.max) * 1;
              this.contextObj.beginPath();
              // 线宽
              this.contextObj.lineWidth = lineW;
              this.contextObj.strokeStyle = this.styleOptions.color;
              this.contextObj.moveTo(item.typeAX, item.typeAY);
              this.contextObj.lineTo(randomOpItem.typeAX, randomOpItem.typeAY);
              this.contextObj.stroke();
            }
          }
        }
      });
      var animationFn = this.getRequestAnimationFrameFn();
      animationFn(this.drawLineFn);
    },
    // 清空画布
    clearCanvas() {
      this.contextObj.clearRect(0, 0, this.canWidth, this.canHeight);
    },
  },
};
</script>

<style lang="scss" scoped>
.lbg-wrapper{
  box-sizing: border-box;
  position: relative;
  width: 100%;
  height: 100%;
}</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝莓味的口香糖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值