【canvas】网易云音乐鲸云动效『孤独星球』的简单实现

本文详细解析了网易云音乐中『孤独星球』特效的实现原理,利用canvas绘制动态涟漪背景,通过控制空心圆和小球的动态变化,营造出迷人的视觉效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

书接上回:【canvas】网易云音乐鲸云特效『水晶音波』的简单实现

今天,我来实现一下『孤独星球』这一动效。

这是网易云音乐的截图,结尾放我自己实现的效果
在这里插入图片描述

上回已经开好“局”了,所以这里就不必重复了,直接讲如何用canvas画背景的涟漪效果。




canvas

这回我细讲思路。
先定义一个空心圆,这空心圆上有个小球
所以要有这么几个关键变量要有,

变量
空心圆圆心位置、变大的速度、半径、渐变的范围(即圆最多有多大)
小球位置、移动的速度、半径.

空心圆会变大,小球会移动,那么实现方式就简单了,我延续上次定义三角形的方式,这样定义

class Circle {
  constructor(context, speed, pole, radius, range) {
    this.ctx = context;
    this.speed = speed;
    this.pole = pole;
    this.radius = radius;
    this.range = range;
    this.__restart();
  }

  __restart() {
    this.r = this.r ? this.radius[0] : this.radius[1];
    this.ballRadius = Math.floor(4 + Math.random() * 3);
    this.ballAngle = Math.random() * PI2; // 小球的位置用角度表示,随机产生
    this.ballPoint || (this.ballPoint = [0, 0]);
    this.ballPoint[0] = Math.cos(this.ballAngle) * this.r; // 根据空心圆的半径计算小球的坐标
    this.ballPoint[1] = Math.sin(this.ballAngle) * this.r;
  }

  __lerp(src, dst, coeff) {
    return src + (dst - src) * coeff; // 线性函数,小学数学
  }

  __update() {
    if (this.r - this.range > 0.0001)  // 空心圆超过一定范围就会重新开始
      this.__restart();
    else {
      this.r += this.speed;
      this.ballAngle += .01;
      this.ballPoint[0] = Math.cos(this.ballAngle) * this.r;
      this.ballPoint[1] = Math.sin(this.ballAngle) * this.r;
      this.opacity = this.__lerp(1, 0, this.r / this.range);
    }
  }

  render() {
    this.__update();
    // 绘制空心圆
    this.ctx.lineWidth = 2;
    this.ctx.strokeStyle = `rgba(179, 179, 179, ${this.opacity})`;
    this.ctx.beginPath();
    this.ctx.arc(this.pole[0], this.pole[1], this.r, 0, PI2);
    this.ctx.stroke();
    // 绘制小球
    this.ctx.strokeStyle = `rgba(179, 179, 179, 0)`;
    this.ctx.fillStyle = `rgba(179, 179, 179, ${this.opacity})`;
    this.ctx.beginPath();
    this.ctx.arc(this.pole[0] + this.ballPoint[0], this.pole[1] + this.ballPoint[1], this.ballRadius, 0, PI2);
    this.ctx.stroke();
    this.ctx.fill();
  }
}

在上述代码中,会有几个值得细品的地方:

  1. 为什么这样计算小球的[x, y]位置写成这样?
this.ballPoint || (this.ballPoint = [0, 0]);
this.ballPoint[0] = Math.cos(this.ballAngle) * this.r;
this.ballPoint[1] = Math.sin(this.ballAngle) * this.r;

  而不这样

this.ballPoint = [Math.cos(this.ballAngle) * this.r, Math.sin(this.ballAngle) * this.r];

这两种方法不会差太多,但我是个强迫症,后者会反复创建新的数组赋值给this.ballPoint,我认为这是不好的,数组只需要创建一次,每次修改数组中的值就可以了。

  1. 为什么构造这个圆时,我传入的半径radius是一个数组?

其实这是为了实现错落有致。在后面的“场景”定义中会进一步解答这个问题。

紧接是定义“场景”。

class Scene {
  constructor(canvas) {
    this.cvs = canvas;
    this.ctx = canvas.getContext('2d');
    const slit = 50;
    const pole = this.cvs.width / 2;
    this.circleSet = [];
    this.circleNum = Math.floor(pole / slit);
    const range = this.circleNum * slit;
    for (let i = 1; i < this.circleNum; ++i)
      this.circleSet.push(new Circle(this.ctx, 1, [pole, pole], [slit, slit * i], range));
  }

  render() {
    this.ctx.clearRect(0, 0, this.cvs.width, this.cvs.height);
    this.circleSet.forEach(circle => circle.render());
  }

  run() {
    if (!this.timer) {
      this.timer = setInterval(this.render.bind(this), 25);
    }
  }

  stop() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = 0;
    }
  }
}

// 跑场景
const canvas = document.getElementById('background');
canvas.width = canvas.height = Math.ceil(canvas.parentNode.lastElementChild.offsetWidth * 1.68421);
const scene = new Scene(canvas);
scene.run();

其中

    const slit = 50;
    const pole = this.cvs.width / 2;
    this.circleSet = [];
    this.circleNum = Math.floor(pole / slit);
    const range = this.circleNum * slit;
    for (let i = 1; i < this.circleNum; ++i)
      this.circleSet.push(new Circle(this.ctx, 1, [pole, pole], [slit, slit * i], range));

这部分是原理,我画了一个简易的示意图表示
在这里插入图片描述
当某个空心圆的半径超过range,那就会到因Circle.__update()方法而回到示意图中start位置。以此实现周期。

最终效果

源码链接在这:github
在线演示:codepen
在这里插入图片描述


下篇 —— 网易云音乐鲸云动效『迷幻水波』的原理:【canvas】三阶贝塞尔曲线拟合圆

### Win11 计算机无法访问网络中其他设备的共享文件夹解决方案 对于Win11计算机无法访问网络中其他设备的共享文件夹的问题,有多种可能的原因以及相应的解决办法。 #### 一、检查网络连接状态 确保所有涉及的设备都已正确连接到一个局域网,并且能够互相Ping通。这一步骤有助于排除基本的物理层或数据链路层面的问题[^4]。 #### 二、启用并配置Windows防火墙例外规则 有时,默认的安全策略可能会阻止对远程资源的访问。因此,在控制面板->系统和安全->Windows Defender 防火墙下找到允许应用通过防火墙选项,确认已经启用了“文件和打印机共享”的入站规则[^1]。 #### 三、调整组策略设置 如果是在企业环境中遇到此问题,则可能是由于某些特定的企业级安全措施所致。可以通过编辑本地组策略(gpedit.msc),导航至`计算机配置 -> Windows 设置 -> 安全设置 -> 本地策略 -> 用户权限分配`, 然后查看是否有针对当前用户的限制[^3]。 #### 四、修改注册表项以支持旧版SMB协议 部分较老的操作系统版本如XP/Server2003默认使用的SMBv1协议已被微软标记为不推荐使用;然而为了兼容性考虑,可以在Win11上手动开启对该协议的支持: 1. 打开运行对话框(`Win + R`) 2. 输入`regedit`启动注册表编辑器 3. 寻找路径 `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters` 4. 新建DWORD(32位)值命名为`EnableInsecureGuestLogons` 并将其数值设为1 ```powershell New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" -PropertyType DWORD -Force ``` #### 五、尝试禁用快速用户切换功能 当启用了快速用户切换时,它可能导致会话之间的冲突从而影响到网络资源共享的功能。关闭该特性通常能有效改善这一状况[^2]. #### 六、重启相关服务 最后但样重要的是,记得重新启动以下几项关键的服务组件以便使更改生效: - Function Discovery Resource Publication - SSDP Discovery Service (Simple Service Discovery Protocol) - UPnP Device Host (Universal Plug and Play) 这些操作完成后再次测试是否可以成功获取目标主机上的共享目录列表。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值