J*aScript Canvas实现可旋转多等分圆形的频闪效应模拟


javascript canvas实现可旋转多等分圆形的频闪效应模拟

本教程旨在指导如何修改现有J*aScript Canvas代码,以实现将圆形等分为多份并进行旋转,从而更准确地模拟频闪效应。文章将详细解释如何从圆心绘制多条分割线来替代原始的直径绘制方式,并提供修改后的代码示例,帮助开发者解决特定采样频率下180度视觉偏差的问题,并为实现更多等分和自定义颜色提供基础。

引言:频闪效应与圆形分割

频闪效应(Stroboscopic Effect)是一种常见的视觉现象,当观察快速旋转的物体在间歇性光照或采样下时,物体可能看起来静止、反向旋转或以不同速度旋转。在Web前端,我们可以利用J*aScript的Canvas API来模拟这一效果。

原始的代码示例旨在展示频闪效应,但其默认的圆形分割方式是绘制一条穿过圆心的直径(即180度分割)。这种方式在某些特定采样频率下,尤其当输入频率与采样频率接近时,可能无法清晰地展示出物体在180度相位上变化的频闪特性,导致视觉上的混淆或无法观察到预期的效果。为了更灵活、更准确地观察和控制频闪效应,我们需要将圆形划分为更多等份,例如3份或更多,并确保这些分割线能够正确旋转。

核心问题分析:多等分与180°采样问题

用户提出的核心需求是:

  1. 将圆形划分为3个或更多等份。
  2. 其中一半分割线能够显示不同的颜色(尽管提供的解决方案主要侧重于分割逻辑,而非复杂的颜色区分)。
  3. 解决在输入频率与采样频率相同时,采样可能出现180度偏差导致视觉效果不准确的问题。

原始代码通过绘制一个从圆周一点到其对角点的线来表示旋转物体,这实际上是将其分成了两部分。要实现多等分,并正确显示每个部分的旋转,我们需要改变绘制策略。关键在于,当绘制多条分割线时,每条线都应该从圆心出发,延伸到圆周上的一个点。

解决方案:从中心绘制多条分割线

为了将圆形分割成N个等份,并让这些分割线随着圆的旋转而移动,我们需要对 render() 函数中的绘制逻辑进行修改。原始代码的绘制方式是 moveTo 到圆周上的一点,然后 lineTo 到其对角点,这本质上是绘制一条直径。当我们需要多条分割线时,这种方法不再适用。正确的做法是:

  1. 确定圆心坐标。
  2. 对于每一个分割点,从圆心 moveTo 到圆心本身。
  3. 然后从圆心 lineTo 到圆周上该分割点对应的位置。

这样,每条分割线都将是半径,并且它们会以圆心为轴心旋转。对于3等分,每条分割线之间的角度差为 360 / 3 = 120 度。

代码实现细节:render 函数的修改

以下是针对 stroboscopic_effect.js 文件中 render() 函数的关键修改部分。

LALAL.AI LALAL.AI

AI人声去除器和声乐提取工具

LALAL.AI 196 查看详情 LALAL.AI
function render() {
    context.fillStyle = "#000000";
    context.fillRect(0, 0, canvas_width, canvas_height);

    context.strokeStyle = "#ffffff";
    context.beginPath();
    context.moveTo(canvas_width / 2, 0);
    context.lineTo(canvas_width / 2, canvas_height);
    context.stroke();

    context.strokeStyle = "#ff51ff";
    context.beginPath();

    /* 左侧旋转圆的绘制逻辑修改 */
    const x1 = canvas_width / 4, y1 = canvas_height / 2 + y_offset; // 左侧圆的圆心
    context.moveTo(x1, y1); // 移动到圆心
    // 绘制第一条分割线
    context.lineTo(x1 + wheel_radius * Math.cos(toRadian(wheel_angle)), y1 - wheel_radius * Math.sin(toRadian(wheel_angle)));
    context.moveTo(x1, y1); // 移动回圆心
    // 绘制第二条分割线 (120度偏移)
    context.lineTo(x1 + wheel_radius * Math.cos(toRadian(wheel_angle + 120)), y1 - wheel_radius * Math.sin(toRadian(wheel_angle + 120)));
    context.moveTo(x1, y1); // 移动回圆心
    // 绘制第三条分割线 (240度偏移)
    context.lineTo(x1 + wheel_radius * Math.cos(toRadian(wheel_angle + 240)), y1 - wheel_radius * Math.sin(toRadian(wheel_angle + 240)));

    context.stroke(); // 提交绘制

    context.beginPath();
    context.arc(canvas_width / 4, canvas_height / 2 + y_offset, wheel_radius, 0, 2 * Math.PI);
    context.stroke();

    /* 右侧采样圆的绘制逻辑修改 */
    context.strokeStyle = "#00ff00";
    context.beginPath();
    const x2 = 3 * canvas_width / 4, y2 = y1; // 右侧圆的圆心,与左侧圆y坐标相同
    context.moveTo(x2, y2); // 移动到圆心
    // 绘制第一条分割线
    context.lineTo(x2 + wheel_radius * Math.cos(toRadian(camera_angle)), y1 - wheel_radius * Math.sin(toRadian(camera_angle)));
    context.moveTo(x2, y2); // 移动回圆心
    // 绘制第二条分割线 (120度偏移)
    context.lineTo(x2 + wheel_radius * Math.cos(toRadian(camera_angle + 120)), y2 - wheel_radius * Math.sin(toRadian(camera_angle + 120)));
    context.moveTo(x2, y2); // 移动回圆心
    // 绘制第三条分割线 (240度偏移)
    context.lineTo(x2 + wheel_radius * Math.cos(toRadian(camera_angle + 240)), y2 - wheel_radius * Math.sin(toRadian(camera_angle + 240)));

    context.stroke(); // 提交绘制

    context.beginPath();
    context.arc(3 * canvas_width / 4, canvas_height / 2 + y_offset, wheel_radius, 0, 2 * Math.PI);
    context.stroke();

    // ... (其他文本和Canvas设置保持不变)
}

修改说明:

  1. 定义圆心: 首先,我们为左右两个圆分别定义了圆心坐标 (x1, y1) 和 (x2, y2)。
  2. context.moveTo(x, y) 到圆心: 在绘制每条分割线之前,都使用 context.moveTo(x, y) 将画笔移动到当前圆的圆心。这是实现从中心向外绘制的关键。
  3. context.lineTo(x, y) 到圆周: 接着,使用 context.lineTo(x, y) 绘制一条从圆心到圆周上特定角度点的线。
    • wheel_angle 或 camera_angle 代表当前旋转角度。
    • wheel_angle + 120 和 wheel_angle + 240 分别表示相对于当前角度偏移120度和240度的位置,从而实现3等分。
    • toRadian() 函数将角度转换为弧度,因为 Math.cos() 和 Math.sin() 接受弧度作为参数。
  4. 重复绘制: 对于每个分割线,都需要重复 moveTo 到圆心,然后 lineTo 到新的圆周点。

通用化与扩展:N等分与颜色定制

N等分

要将圆形分割成 N 个等份,只需将 120 和 240 等固定角度替换为基于 N 的计算:

// 假设 divisions 是你想要分割的份数 (例如 3, 4, 6 等)
const divisions = 3; // 可以是任何大于2的整数
const angleIncrement = 360 / divisions;

for (let i = 0; i < divisions; i++) {
    const currentAngle = wheel_angle + i * angleIncrement;
    context.moveTo(x1, y1);
    context.lineTo(x1 + wheel_radius * Math.cos(toRadian(currentAngle)), y1 - wheel_radius * Math.sin(toRadian(currentAngle)));
}

通过这种循环结构,你可以轻松地将圆分割成任意数量的等份,使其更具通用性。

颜色定制

如果需要实现“一半分割线是不同颜色”的需求,则需要更精细的绘制控制。例如,如果想让某个扇形区域或某条分割线具有不同颜色,可以这样做:

  1. 绘制不同颜色的分割线: 在循环中,根据 i 的值判断是否更改 context.strokeStyle。
  2. 绘制不同颜色的扇形区域: 如果目标是让分割出的“扇形”区域具有不同颜色,则需要使用 context.arc() 结合 context.lineTo() 和 context.fill() 来绘制和填充扇形,而不是简单地绘制线。这会比仅仅绘制分割线复杂一些,需要计算每个扇形的起始和结束角度。

例如,绘制一个特定颜色的扇形:

// 假设要绘制第一个120度扇形为红色
context.fillStyle = "red";
context.beginPath();
context.moveTo(x1, y1);
context.arc(x1, y1, wheel_radius, toRadian(wheel_angle), toRadian(wheel_angle + angleIncrement));
context.closePath();
context.fill();

// 其他扇形或线条继续绘制

完整代码示例

为了提供一个完整的可运行示例,下面将整合修改后的J*aScript代码。HTML部分保持不变,因为它主要负责设置Canvas元素和用户交互控件。

stroboscopic_effect.js (修改后)

let wheel_angle, camera_angle;
let wheel_rpm, angular_speed;
let wheel_radius;
let frames_skip, cooldown, sampling_rate, frame_no;
let is_paused, direction;
/* original code from  */
/* https://visualize-it.github.io/stroboscopic_effect/simulation.html */

function update() {
    wheel_angle += direction * angular_speed;
    if (wheel_angle > 360) {
        wheel_angle -= 360;
    }
    else if (wheel_angle < 0) {
        wheel_angle += 360;
    }

    if (frame_no == 0) {
        camera_angle = wheel_angle;
        frame_no = frames_skip;
    }
    else {
        frame_no -= 1;
    }
}

function render() {
    context.fillStyle = "#000000";
    context.fillRect(0, 0, canvas_width, canvas_height);

    context.strokeStyle = "#ffffff";
    context.beginPath();
    context.moveTo(canvas_width / 2, 0);
    context.lineTo(canvas_width / 2, canvas_height);
    context.stroke();

    context.strokeStyle = "#ff51ff";
    context.beginPath();

    /* 左侧旋转圆的绘制逻辑修改 */
    const x1 = canvas_width / 4, y1 = canvas_height / 2 + y_offset; // 左侧圆的圆心
    context.moveTo(x1, y1); // 移动到圆心
    // 绘制第一条分割线
    context.lineTo(x1 + wheel_radius * Math.cos(toRadian(wheel_angle)), y1 - wheel_radius * Math.sin(toRadian(wheel_angle)));
    context.moveTo(x1, y1); // 移动回圆心
    // 绘制第二条分割线 (120度偏移)
    context.lineTo(x1 + wheel_radius * Math.cos(toRadian(wheel_angle + 120)), y1 - wheel_radius * Math.sin(toRadian(wheel_angle + 120)));
    context.moveTo(x1, y1); // 移动回圆心
    // 绘制第三条分割线 (240度偏移)
    context.lineTo(x1 + wheel_radius * Math.cos(toRadian(wheel_angle + 240)), y1 - wheel_radius * Math.sin(toRadian(wheel_angle + 240)));

    context.stroke(); // 提交绘制

    context.beginPath();
    context.arc(canvas_width / 4, canvas_height / 2 + y_offset, wheel_radius, 0, 2 * Math.PI);
    context.stroke();

    /* 右侧采样圆的绘制逻辑修改 */
    context.strokeStyle = "#00ff00";
    context.beginPath();
    const x2 = 3 * canvas_width / 4, y2 = y1; // 右侧圆的圆心,与左侧圆y坐标相同
    context.moveTo(x2, y2); // 移动到圆心
    // 绘制第一条分割线
    context.lineTo(x2 + wheel_radius * Math.cos(toRadian(camera_angle)), y1 - wheel_radius * Math.sin(toRadian(camera_angle)));
    context.moveTo(x2, y2); // 移动回圆心
    // 绘制第二条分割线 (120度偏移)
    context.lineTo(x2 + wheel_radius * Math.cos(toRadian(camera_angle + 120)), y2 - wheel_radius * Math.sin(toRadian(camera_angle + 120)));
    context.moveTo(x2, y2); // 移动回圆心
    // 绘制第三条分割线 (240度偏移)
    context.lineTo(x2 + wheel_radius * Math.cos(toRadian(camera_angle + 240)), y2 - wheel_radius * Math.sin(toRadian(camera_angle + 240)));

    context.stroke(); // 提交绘制

    context.beginPath();
    context.arc(3 * canvas_width / 4, canvas_height / 2 + y_offset, wheel_radius, 0, 2 * Math.PI);
    context.stroke();

    if (mobile) {
        context.font = "15px Arial";
    }
    else {
        context.font = "30px Arial";
    }
    context.textAlign = "center";
    context.fillStyle = "#ffffff";
    context.fillText("Base", canvas_width / 4, 30);
    context.fillText("Teste Aliasing", 3 * canvas_width / 4, 30);
}

function initParams() {
    wheel_angle = Math.random() * 360;
    wheel_rpm = 60;
    rpm_slider.value = wheel_rpm;
    calcSpeed();
    //rpm_display.innerHTML = `Wheel speed: ${wheel_rpm} RPM or ${(angular_speed * fps / 360).toFixed(2)} revolution(s) per second`;
    rpm_display.innerHTML = `Frequência do cículo base: ${(angular_speed * fps / 360).toFixed(2)} Hz (Hertz)`;

    frames_skip = 26;
    calcCooldown();
    fps_slider.value = 60 - frames_skip;
    //fps_display.innerHTML = `Sampling rate: ${sampling_rate.toFixed(2)} Hz; Sampling Time: ${cooldown.toFixed(2)} seconds`;
    fps_display.innerHTML = `Frequência de Amostragem: ${sampling_rate.toFixed(2)} Hz;`;

    wheel_radius = (canvas_width / 4) - 20;
    frame_no = 0;
    paused = false;
    direction = -1;
}

function updateParams(variable) {
    if (variable == 'rpm') {
        wheel_rpm = rpm_slider.value;
        calcSpeed();
        //rpm_display.innerHTML = `Wheel speed: ${wheel_rpm} RPM or ${(angular_speed * fps / 360).toFixed(2)} revolution(s) per second`;
        rpm_display.innerHTML = `Frequência do cículo base: ${(angular_speed * fps / 360).toFixed(2)} Hz (Hertz)`;
    }
    else if (variable == 'fps') {
        frames_skip = 60 - fps_slider.value;
        calcCooldown();
        //fps_display.innerHTML = `Sampling rate: ${sampling_rate.toFixed(2)} Hz; Sampling Time: ${cooldown.toFixed(2)} seconds`;
        fps_display.innerHTML = `Frequência de Amostragem: ${sampling_rate.toFixed(2)} Hz;`;
    }
    else if (variable == 'pause') {
        if (is_paused) {
            is_paused = false;
            pause_button.innerHTML = "Parar";
        }
        else {
            is_paused = true;
            pause_button.innerHTML = "Continuar";
        }
    }
    else if (variable == 'dir') {
        direction *= (-1);
    }
}

function simulate(number) {
    if (number == 1) {
        rpm_slider.value = 80;
        fps_slider.value = 59;
    }
    else if (number == 2) {

以上就是J*aScript Canvas实现可旋转多等分圆形的频闪效应模拟的详细内容,更多请关注其它相关文章!


# 每条  # vue框seo  # 新歌网站建设银行  # 网站制作与推广实训报告  # 招聘系统网站建设  # seo产品怎么发  # 南通市网站推广方案厂家  # 上马seo网站推广  # 武侯区seo排名最好  # 济宁正规网络推广网站  # 洪江关键词网站优化  # 更准确  # 半分  # 划分为  # 多条  # javascript  # 第三条  # 第二条  # 第一条  # 多等  # 分割线  # red  # canva  # cos  # github  # git  # 前端  # js  # html  # java 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: Animex动漫社正版在线入口 Animex动漫社动漫官方观看网  如何在CSS中设置背景图像:一个全面指南  优化2xN网格最大路径和的动态规划算法实践  为什么XML解析器对大小写敏感? 理解XML规范中的大小写规则与最佳实践  mysql如何限制远程访问_mysql远程访问限制方法  MacBook Pro词典使用指南  抖音视频如何添加标题?添加标题有哪些好处?  酷狗音乐多音轨设置教程  邮政快递寄件查询入口 邮政快递收件查询入口  《三国:谋定天下》平民全阶段通用阵容  AO3官方镜像链接 | 最新防走失网址永久收藏  C++ static关键字作用_C++静态成员变量与静态函数  美发店速赢秘籍  如何自定义苹果手机铃声  抖音赚钱快速入门_新手必看的抖音赚钱步骤  J*a中为什么强调组合优于继承_组合模式带来的灵活性与可维护性解析  汽水音乐官方网站登录入口_汽水音乐网页版进入链接  纯CSS实现自适应宽度与响应式布局的水平按钮组  Python对象引用与属性赋值:理解链表中的行为  抖音小程序怎么开通?小程序开通条件是什么?  《U校园》学生登录入口2025  漫蛙manwa官网浏览入口_漫蛙漫画网页版访问链接  J*aScript:从子元素中批量移除特定CSS类  汽水音乐在线听歌网页版 汽水音乐在线听歌网页版入口  微信注销后银行卡解绑了吗_微信注销后银行卡解绑状态  Win11怎么设置分辨率 Win11显示设置调整分辨率及刷新率修改  windows10怎么设置电源按钮_windows10按下电源键功能修改  铁路12306入口 铁路12306官网版入口登录网址  悟空浏览器网页版在线工具 悟空浏览器网页版在线平台入口  怎么恢复删除的电脑文件_数据恢复软件使用教程  J*aScript事件处理:优化键盘输入与表单提交的实践指南  如何测试您的网站全球打开速度-网站海外测速工  三星M34录音变声问题_Samsung M34麦克风调整  mysql通配符能用于日志查询吗_mysql通配符在系统日志查询中的实际使用方法  Animex动漫社社登录官网 Animex动漫社资源社入口直达  谷歌浏览器怎么把网页翻译成中文_Chrome网页翻译功能使用方法  QQ网页版官方账号登录入口 QQ网页版网页版入口快速导航  J*aScript装饰器_元编程实战  Linux如何自动分析系统异常日志_Linux日志智能检测  传统曲艺莲花落的表演形式是  在Django单元测试中优雅处理信号:基于环境的条件执行策略  Go Template中优雅处理循环最后一项:自定义函数实践  oppo手机如何通过下拉通知栏截图_oppo手机通知栏快捷截图方法  《随手记》备份数据方法  如何使用CSS Grid实现“大方块左侧,小方块右侧垂直堆叠”的水平布局  暴风影音官网正式版_暴风影音手机版官网下载安卓  mysql镜像配置如何恢复数据_mysql镜像配置数据恢复详细流程  太平年在哪个平台播出  使用AI在VS Code中将代码从一种语言翻译成另一种  我的世界游戏平台入口 我的世界官方官网直达链接 

 2025-11-15

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.