I found this code on the internet (font: Visualize It) and it already works close to what I need. However, to ensure that it works 100%, I need it to be divided into 3 parts or more and that half of one of these dividers is a different color.
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 = `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) {
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);
}
<!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>
The purpose of this code is to show the stroboscopic effect, however, with this code I cannot show the effect when the input frequency is the same sampling frequency, in this condition the sampling would vary by 180°.
Please help me, I spent a whole day trying to figure out how to solve it and I couldn't.