You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

频闪效果模拟代码优化:圆等分与同频率采样异常修复

频闪效果模拟代码优化需求

我从Visualize It获取了一段频闪效果模拟代码,目前功能已接近需求,但仍需优化两点:

  • 将圆分为3份及以上,且其中一个等分的一半为不同颜色;
  • 修复输入频率与采样频率相同时,无法呈现采样偏移180°的频闪效果。

以下是现有代码:

JavaScript 代码

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;

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();
    context.moveTo(canvas_width / 4 + wheel_radius * Math.cos(toRadian(wheel_angle)), canvas_height / 2 - wheel_radius * Math.sin(toRadian(wheel_angle)) + y_offset);
    if (wheel_angle >= 180) {
        context.lineTo(canvas_width / 4 + wheel_radius * Math.cos(toRadian(wheel_angle - 180)), canvas_height / 2 - wheel_radius * Math.sin(toRadian(wheel_angle - 180)) + y_offset);
    }
    else {
        context.lineTo(canvas_width / 4 + wheel_radius * Math.cos(toRadian(wheel_angle + 180)), canvas_height / 2 - wheel_radius * Math.sin(toRadian(wheel_angle + 180)) + y_offset);
    }
    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();
    context.moveTo(3 * canvas_width / 4 + wheel_radius * Math.cos(toRadian(camera_angle)), canvas_height / 2 - wheel_radius * Math.sin(toRadian(camera_angle)) + y_offset);
    if (wheel_angle >= 180) {
        context.lineTo(3 * canvas_width / 4 + wheel_radius * Math.cos(toRadian(camera_angle - 180)), canvas_height / 2 - wheel_radius * Math.sin(toRadian(camera_angle - 180)) + y_offset);
    }
    else {
        context.lineTo(3 * canvas_width / 4 + wheel_radius * Math.cos(toRadian(camera_angle + 180)), canvas_height / 2 - wheel_radius * Math.sin(toRadian(camera_angle + 180)) + y_offset);
    }
    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 = `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 = `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 = `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 = `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) {
        rpm_slider.value = 120;
        fps_slider.value = 48;
    }
    else if (number == 3) {
        rpm_slider.value = 120;
        fps_slider.value = 46;
    }
    else if (number == 4) {
        rpm_slider.value = 100;
        fps_slider.value = 55;
    }
    else if (number == 5) {
        rpm_slider.value = 100;
        fps_slider.value = 41;
    }
    else if (number == 6) {
        rpm_slider.value = 60;
        fps_slider.value = 1;
    }
    updateParams("rpm");
    updateParams("fps");
    window.scrollTo(0, 40);
}

function step() {
    if (!is_paused) {
        update();
    }
    render();
    animate(step);
}

function calcSpeed() {
    angular_speed = (wheel_rpm * 360) / (fps * 60);
}

function calcCooldown() {
    cooldown = (frames_skip + 1) / fps;
    sampling_rate = 1 / cooldown;
}

function toRadian(degree) {
    return (Math.PI * degree / 180);
}

let screen_width = window.innerWidth, screen_height = window.innerHeight;
let y_offset = 30;
let fps = 60;

let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");

let pause_button = document.getElementById("pause-button");

let rpm_display = document.getElementById("rpm-display");
let rpm_slider = document.getElementById("rpm-slider");

let fps_slider = document.getElementById("fps-slider");
let fps_display = document.getElementById("fps-display");

if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
    mobile = true;
} else {
    mobile = false;
}

if (mobile) {
    canvas_width = 0.9 * screen_width;
}
else {
    canvas_width = 0.5 * screen_width;
}
canvas_height = canvas_width / 2 + y_offset;

canvas.width = canvas_width;
canvas.height = canvas_height;

let animate = window.requestAnimationFrame
    || window.webkitRequestAnimationFrame
    || window.mozRequestAnimationFrame
    || function (callback) {
        window.setTimeout(callback, 1000 / (fps));
    };

window.onload = function() {
    initParams();
    animate(step);
}

HTML 代码

<!DOCTYPE html>
<html lang="en-US">

<head>
  <title>Stroboscopic Effect | Visualize It</title>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />

  <!-- Materialize -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>

  <!-- Jquery -->
  <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>

  <!-- CSS -->
  <link rel="stylesheet" href="../style.css" />
  <script src="basic.js" defer></script>
  <script src="stroboscopic_effect.js" defer></script>
</head>

<script async src="https://www.googletagmanager.com/gtag/js?id=G-M95CKRP8HB"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag() { window.dataLayer.push(arguments); }
  gtag('js', new Date());

  gtag('config', 'G-M95CKRP8HB');
</script>

<body>

  <div class="text">
    <br>
    <div class="container" style="width:90%">
      <div class="row">
        <div class="col s12 l8">
          <canvas id="canvas"></canvas>
        </div>
        <div class="col s12 l4">
          <center>
            <p id="rpm-display"></p>
            <input type="range" min="0" max="200" value="30" id="rpm-slider" oninput="updateParams('rpm')"
              onchange="updateParams('rpm')" class="slider">
            <p id="fps-display"></p>
            <input type="range" min="0" max="59" value="30" id="fps-slider" oninput="updateParams('fps')"
              onchange="updateParams('fps')" class="slider">
          </center>
        </div>
        <div class="col s12 l4">
          <button type="button" class="btn btn-danger" onclick="simulate(3);">Estacionáro</button>
          <button type="button" class="btn btn-success" onclick="simulate(1);">Normal</button>
          <button type="button" class="btn btn-success" onclick="simulate(5);">Normal lento</button>
          <button type="button" class="btn btn-success" onclick="simulate(6);">Alternado 180</button>
          <button type="button" class="btn btn-success" onclick="simulate(2);">Contrário Lento</button>
        </div>
      </div>
    </div>
  </div>
</body>

</html>

内容的提问来源于stack exchange,提问作者Sillas Senna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 22:45:36