refactor: 얇게 남아있던 별의 꼬리가 사라지도록 수정
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
|||||||
shouldRecycleFlightStar,
|
shouldRecycleFlightStar,
|
||||||
} from "@/features/flight-starfield/model/starfieldModel";
|
} from "@/features/flight-starfield/model/starfieldModel";
|
||||||
import { FlightStar } from "@/features/flight-starfield/model/types";
|
import { FlightStar } from "@/features/flight-starfield/model/types";
|
||||||
|
import { FLIGHT_STARFIELD_TUNING } from "@/shared/config/starfield";
|
||||||
import { clamp } from "@/shared/lib/math/number";
|
import { clamp } from "@/shared/lib/math/number";
|
||||||
import { getPrefersReducedMotionMediaQuery } from "@/shared/lib/motion/prefersReducedMotion";
|
import { getPrefersReducedMotionMediaQuery } from "@/shared/lib/motion/prefersReducedMotion";
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ export function FlightStarfieldCanvas({
|
|||||||
let width = window.innerWidth;
|
let width = window.innerWidth;
|
||||||
let height = window.innerHeight;
|
let height = window.innerHeight;
|
||||||
let animationFrameId = 0;
|
let animationFrameId = 0;
|
||||||
|
let dpr = 1;
|
||||||
|
|
||||||
const motionQuery = getPrefersReducedMotionMediaQuery();
|
const motionQuery = getPrefersReducedMotionMediaQuery();
|
||||||
let prefersReducedMotion = motionQuery.matches;
|
let prefersReducedMotion = motionQuery.matches;
|
||||||
@@ -48,12 +50,21 @@ export function FlightStarfieldCanvas({
|
|||||||
const vanishXJitter = vanishXJitterRef.current ?? 0;
|
const vanishXJitter = vanishXJitterRef.current ?? 0;
|
||||||
|
|
||||||
const setCanvasSize = () => {
|
const setCanvasSize = () => {
|
||||||
width = window.innerWidth;
|
const viewportWidth = window.visualViewport?.width ?? window.innerWidth;
|
||||||
height = window.innerHeight;
|
const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
|
||||||
canvas.width = width;
|
width = Math.max(1, Math.floor(viewportWidth));
|
||||||
canvas.height = height;
|
height = Math.max(1, Math.floor(viewportHeight));
|
||||||
|
canvas.style.width = `${width}px`;
|
||||||
|
canvas.style.height = `${height}px`;
|
||||||
|
dpr = window.devicePixelRatio || 1;
|
||||||
|
canvas.width = Math.max(1, Math.floor(width * dpr));
|
||||||
|
canvas.height = Math.max(1, Math.floor(height * dpr));
|
||||||
|
context.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||||
|
context.imageSmoothingEnabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const snapToDevicePixel = (value: number) => Math.round(value * dpr) / dpr;
|
||||||
|
|
||||||
const getVanishingPoint = () =>
|
const getVanishingPoint = () =>
|
||||||
createVanishingPoint({
|
createVanishingPoint({
|
||||||
width,
|
width,
|
||||||
@@ -102,12 +113,20 @@ export function FlightStarfieldCanvas({
|
|||||||
const deltaX = toX - fromX;
|
const deltaX = toX - fromX;
|
||||||
const deltaY = toY - fromY;
|
const deltaY = toY - fromY;
|
||||||
const movementLength = Math.hypot(deltaX, deltaY);
|
const movementLength = Math.hypot(deltaX, deltaY);
|
||||||
|
const tailLineWidth = clamp(star.radius * 0.9, 0.65, 1.6);
|
||||||
|
const canDrawTail =
|
||||||
|
star.tailLength >= FLIGHT_STARFIELD_TUNING.tail.cleanup.minTailLengthToDraw &&
|
||||||
|
movementLength >= FLIGHT_STARFIELD_TUNING.tail.cleanup.minMovementToDraw &&
|
||||||
|
visibleAlpha >= FLIGHT_STARFIELD_TUNING.tail.cleanup.minAlphaToDraw &&
|
||||||
|
tailLineWidth >= FLIGHT_STARFIELD_TUNING.tail.cleanup.minLineWidthToDraw;
|
||||||
|
|
||||||
if (star.tailLength < 1 || movementLength < 0.001) {
|
if (!canDrawTail) {
|
||||||
|
const snappedToX = snapToDevicePixel(toX);
|
||||||
|
const snappedToY = snapToDevicePixel(toY);
|
||||||
context.globalAlpha = visibleAlpha;
|
context.globalAlpha = visibleAlpha;
|
||||||
context.fillStyle = "#f8fbff";
|
context.fillStyle = "#f8fbff";
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.arc(toX, toY, star.radius, 0, Math.PI * 2);
|
context.arc(snappedToX, snappedToY, star.radius, 0, Math.PI * 2);
|
||||||
context.fill();
|
context.fill();
|
||||||
context.globalAlpha = 1;
|
context.globalAlpha = 1;
|
||||||
return;
|
return;
|
||||||
@@ -115,26 +134,33 @@ export function FlightStarfieldCanvas({
|
|||||||
|
|
||||||
const directionX = deltaX / movementLength;
|
const directionX = deltaX / movementLength;
|
||||||
const directionY = deltaY / movementLength;
|
const directionY = deltaY / movementLength;
|
||||||
const tailX = toX - directionX * star.tailLength;
|
const tailX = snapToDevicePixel(toX - directionX * star.tailLength);
|
||||||
const tailY = toY - directionY * star.tailLength;
|
const tailY = snapToDevicePixel(toY - directionY * star.tailLength);
|
||||||
|
const snappedToX = snapToDevicePixel(toX);
|
||||||
|
const snappedToY = snapToDevicePixel(toY);
|
||||||
|
|
||||||
const gradient = context.createLinearGradient(tailX, tailY, toX, toY);
|
const gradient = context.createLinearGradient(
|
||||||
|
tailX,
|
||||||
|
tailY,
|
||||||
|
snappedToX,
|
||||||
|
snappedToY,
|
||||||
|
);
|
||||||
gradient.addColorStop(0, "rgba(248, 251, 255, 0)");
|
gradient.addColorStop(0, "rgba(248, 251, 255, 0)");
|
||||||
gradient.addColorStop(1, `rgba(248, 251, 255, ${visibleAlpha})`);
|
gradient.addColorStop(1, `rgba(248, 251, 255, ${visibleAlpha})`);
|
||||||
|
|
||||||
context.strokeStyle = gradient;
|
context.strokeStyle = gradient;
|
||||||
context.lineWidth = clamp(star.radius * 0.9, 0.65, 1.6);
|
context.lineWidth = tailLineWidth;
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.moveTo(tailX, tailY);
|
context.moveTo(tailX, tailY);
|
||||||
context.lineTo(toX, toY);
|
context.lineTo(snappedToX, snappedToY);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
context.globalAlpha = Math.min(1, visibleAlpha + 0.08);
|
context.globalAlpha = Math.min(1, visibleAlpha + 0.08);
|
||||||
context.fillStyle = "#f8fbff";
|
context.fillStyle = "#f8fbff";
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.arc(
|
context.arc(
|
||||||
toX,
|
snappedToX,
|
||||||
toY,
|
snappedToY,
|
||||||
clamp(star.radius * 0.72, 0.6, 1.45),
|
clamp(star.radius * 0.72, 0.6, 1.45),
|
||||||
0,
|
0,
|
||||||
Math.PI * 2,
|
Math.PI * 2,
|
||||||
@@ -177,7 +203,12 @@ export function FlightStarfieldCanvas({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const drawFrame = (moveStars: boolean) => {
|
const drawFrame = (moveStars: boolean) => {
|
||||||
context.fillStyle = "rgba(2, 5, 10, 0.3)";
|
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
|
context.globalAlpha = 1;
|
||||||
|
context.globalCompositeOperation = "source-over";
|
||||||
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
context.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||||
|
context.fillStyle = "rgb(2, 5, 10)";
|
||||||
context.fillRect(0, 0, width, height);
|
context.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
drawCenterVeil();
|
drawCenterVeil();
|
||||||
@@ -235,7 +266,6 @@ export function FlightStarfieldCanvas({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderStatic = () => {
|
const renderStatic = () => {
|
||||||
context.clearRect(0, 0, width, height);
|
|
||||||
drawFrame(false);
|
drawFrame(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -264,6 +294,7 @@ export function FlightStarfieldCanvas({
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("resize", handleResize);
|
window.addEventListener("resize", handleResize);
|
||||||
|
window.visualViewport?.addEventListener("resize", handleResize);
|
||||||
motionQuery.addEventListener("change", handleMotionChange);
|
motionQuery.addEventListener("change", handleMotionChange);
|
||||||
|
|
||||||
if (prefersReducedMotion || isPaused) {
|
if (prefersReducedMotion || isPaused) {
|
||||||
@@ -274,6 +305,7 @@ export function FlightStarfieldCanvas({
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("resize", handleResize);
|
window.removeEventListener("resize", handleResize);
|
||||||
|
window.visualViewport?.removeEventListener("resize", handleResize);
|
||||||
motionQuery.removeEventListener("change", handleMotionChange);
|
motionQuery.removeEventListener("change", handleMotionChange);
|
||||||
stopAnimation();
|
stopAnimation();
|
||||||
};
|
};
|
||||||
@@ -282,7 +314,7 @@ export function FlightStarfieldCanvas({
|
|||||||
return (
|
return (
|
||||||
<canvas
|
<canvas
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
className="fixed inset-0 z-0 bg-black pointer-events-none"
|
className="fixed inset-0 z-0 h-screen w-screen bg-black pointer-events-none"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ export const FLIGHT_STARFIELD_TUNING = {
|
|||||||
pointRange: { min: 0, max: 2.5 },
|
pointRange: { min: 0, max: 2.5 },
|
||||||
shortRange: { min: 2.5, max: 3.8 },
|
shortRange: { min: 2.5, max: 3.8 },
|
||||||
longRange: { min: 4, max: 10 },
|
longRange: { min: 4, max: 10 },
|
||||||
|
cleanup: {
|
||||||
|
minAlphaToDraw: 0.09,
|
||||||
|
minMovementToDraw: 0.18,
|
||||||
|
minLineWidthToDraw: 1,
|
||||||
|
minTailLengthToDraw: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
spawnRadius: {
|
spawnRadius: {
|
||||||
centerChance: 0.08,
|
centerChance: 0.08,
|
||||||
|
|||||||
Reference in New Issue
Block a user