admin管理员组文章数量:1355991
I've been attempting to create a pixel animation as a CSS background. The animation is accomplished through the React component below, and works except for the fact that I cannot seem to get the background image to rescale reponsively to the viewport:
"use client";
import React, { useState, useEffect, useRef } from "react";
import Loader from "@/components/Loader";
import styles from "./Home.module.css";
const spritesheet = "/pixelart/spacesprites.png";
const spriteDataUrl = "/pixelart/spacesprites.json";
export default function AnimatedBackground({ children }) {
const [frame, setFrame] = useState(0);
const [isLoaded, setIsLoaded] = useState(false);
const [spriteData, setSpriteData] = useState(null);
const animationSpeed = 100; // Adjust for desired animation speed (milliseconds)
const animationInterval = useRef(null);
useEffect(() => {
const preloadAssets = async () => {
try {
// Load image
const img = new Image();
img.src = spritesheet;
await img.decode(); // Ensure image is fully loaded
// Load JSON
const res = await fetch(spriteDataUrl);
const data = await res.json();
setSpriteData(data);
// Mark as loaded
setIsLoaded(true);
} catch (error) {
console.error("Failed to load assets:", error);
}
};
preloadAssets();
}, []);
useEffect(() => {
if (!isLoaded || !spriteData) return;
const frames = spriteData.frames;
const frameCount = Object.keys(frames).length;
let currentFrame = 0;
animationInterval.current = setInterval(() => {
setFrame(currentFrame);
currentFrame = (currentFrame + 1) % frameCount;
}, animationSpeed);
return () => clearInterval(animationInterval.current);
}, [isLoaded, spriteData]);
if (!isLoaded || !spriteData) {
return (
<Loader />
);
}
const frameData = spriteData.frames[Object.keys(spriteData.frames)[frame]].frame;
const backgroundStyle = {
backgroundImage: `url(${spritesheet})`,
width: frameData.w,
height: frameData.h,
backgroundPosition: `-${frameData.x}px -${frameData.y}px`,
};
return (
<div
className={`${styles['twinkle-background']}`}
style={backgroundStyle}
>
{children}
</div>
);
};
My corresponding CSS is:
.twinkle-background {
display: flex;
justify-content: center;
image-rendering: crisp-edges;
}
To summarize the problem, each frame in my sprite sheet is 1920 x 1440 pixels. This is what frameData.w
and frameData.h
in the component correspond to. Because the width and height in the inline style is defined as such, however, I'm stumped as to how to ensure that the background image conforms to the dimensions of the viewport.
This component is wrapped in a div with corresponding CSS:
.twinkle {
width: 100vw;
height: auto;
display: flex;
justify-content: center;
}
and if it helps, the corresponding JSON:
{ "frames": {
"space 0.aseprite": {
"frame": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 1.aseprite": {
"frame": { "x": 1920, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 2.aseprite": {
"frame": { "x": 3840, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 3.aseprite": {
"frame": { "x": 5760, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 4.aseprite": {
"frame": { "x": 7680, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 5.aseprite": {
"frame": { "x": 9600, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 6.aseprite": {
"frame": { "x": 11520, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 7.aseprite": {
"frame": { "x": 13440, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 8.aseprite": {
"frame": { "x": 15360, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 9.aseprite": {
"frame": { "x": 17280, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 10.aseprite": {
"frame": { "x": 19200, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 11.aseprite": {
"frame": { "x": 21120, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 12.aseprite": {
"frame": { "x": 23040, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 13.aseprite": {
"frame": { "x": 24960, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
},
"space 14.aseprite": {
"frame": { "x": 26880, "y": 0, "w": 1920, "h": 1440 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 1920, "h": 1440 },
"sourceSize": { "w": 1920, "h": 1440 },
"duration": 100
}
},
"meta": {
"app": "/",
"version": "1.3.13-x64",
"image": "spacesprites.png",
"format": "RGBA8888",
"size": { "w": 28800, "h": 1440 },
"scale": "1",
"frameTags": [
],
"layers": [
{ "name": "Background", "opacity": 255, "blendMode": "normal" },
{ "name": "Stars", "opacity": 255, "blendMode": "normal" },
{ "name": "Stars2", "opacity": 255, "blendMode": "normal" },
{ "name": "Stars3", "opacity": 128, "blendMode": "normal" },
{ "name": "Stars4", "opacity": 128, "blendMode": "normal" },
{ "name": "Stars5", "opacity": 128, "blendMode": "normal" },
{ "name": "MilkyWay01", "opacity": 255, "blendMode": "normal" },
{ "name": "MilkyWay02", "opacity": 255, "blendMode": "normal" },
{ "name": "MilkyWay03", "opacity": 255, "blendMode": "normal" },
{ "name": "MilkyWay04", "opacity": 255, "blendMode": "normal" },
{ "name": "MilkyWay05", "opacity": 255, "blendMode": "normal" },
{ "name": "LargeStars01", "opacity": 255, "blendMode": "normal" },
{ "name": "MediumStars01", "opacity": 255, "blendMode": "normal" },
{ "name": "RedNebula01", "opacity": 255, "blendMode": "normal" },
{ "name": "RedNebula02", "opacity": 255, "blendMode": "normal" },
{ "name": "RedNebula03", "opacity": 255, "blendMode": "normal" },
{ "name": "RedNebula05", "opacity": 255, "blendMode": "normal" },
{ "name": "Planet01", "opacity": 255, "blendMode": "normal" },
{ "name": "Foreground", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar010", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar016", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar011", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar012", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar013", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar014", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar015", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar020", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar021", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar022", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar023", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar024", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar030", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar031", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar032", "opacity": 255, "blendMode": "normal" },
{ "name": "AnimateStar033", "opacity": 255, "blendMode": "normal" }
],
"slices": [
]
}
}
Any advice on how to revise my CSS to achieve the viewport responsiveness would be much appreciated.
本文标签: reactjsResponsive CSS sprite animation backgroundStack Overflow
版权声明:本文标题:reactjs - Responsive CSS sprite animation background - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744039859a2580462.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论