pixel perfect scaling even on high density screens

This commit is contained in:
Corwin 2024-04-29 00:28:17 +01:00
parent 1991fbf056
commit 917d7b3d5a
No known key found for this signature in database
7 changed files with 60 additions and 37 deletions

View file

@ -1,6 +1,6 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import "./globalStyles.css"; import "./globalStyles.css";
import StyledComponentsRegistry from "./registry"; import StyledComponentsRegistry, { BodyPixelRatio } from "./registry";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "agb - a rust framework for making Game Boy Advance games", title: "agb - a rust framework for making Game Boy Advance games",
@ -13,9 +13,9 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html lang="en"> <html lang="en">
<body> <BodyPixelRatio>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry> <StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body> </BodyPixelRatio>
</html> </html>
); );
} }

View file

@ -16,6 +16,8 @@ import { ExternalLink } from "@/components/externalLink";
const HelpLinks = styled.div` const HelpLinks = styled.div`
display: flex; display: flex;
flex-wrap: wrap;
gap: 16px;
justify-content: space-around; justify-content: space-around;
`; `;

View file

@ -1,8 +1,8 @@
"use client"; "use client";
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { useServerInsertedHTML } from "next/navigation"; import { useServerInsertedHTML } from "next/navigation";
import { ServerStyleSheet, StyleSheetManager } from "styled-components"; import styled, { ServerStyleSheet, StyleSheetManager } from "styled-components";
export default function StyledComponentsRegistry({ export default function StyledComponentsRegistry({
children, children,
@ -27,3 +27,18 @@ export default function StyledComponentsRegistry({
</StyleSheetManager> </StyleSheetManager>
); );
} }
const BodyWithPixelRatio = styled.body<{
$pixel?: number;
}>`
--device-pixel: calc(1px / ${(props) => props.$pixel});
`;
export function BodyPixelRatio({ children }: { children: React.ReactNode }) {
const [pixel, setPixel] = useState(1);
useEffect(() => {
setPixel(window.devicePixelRatio);
}, []);
return <BodyWithPixelRatio $pixel={pixel}>{children}</BodyWithPixelRatio>;
}

View file

@ -3,13 +3,11 @@ import { Games, ShowcaseGame } from "../games";
import { ContentBlock } from "@/components/contentBlock"; import { ContentBlock } from "@/components/contentBlock";
import { ExternalLink } from "@/components/externalLink"; import { ExternalLink } from "@/components/externalLink";
import Link from "next/link"; import Link from "next/link";
import Image, { StaticImageData } from "next/image";
import { import {
ScreenshotsWrapper,
ScreenshotWrapper,
BackToShowcaseWrapper, BackToShowcaseWrapper,
DescriptionAndScreenshots, DescriptionAndScreenshots,
Description, Description,
Screenshots,
} from "./styled"; } from "./styled";
export async function generateStaticParams() { export async function generateStaticParams() {
@ -51,18 +49,6 @@ function DeveloperNames({ names }: { names: string[] }) {
return first.join(", ") + `, and ${names[names.length - 1]}`; return first.join(", ") + `, and ${names[names.length - 1]}`;
} }
function Screenshots({ screenshots }: { screenshots: StaticImageData[] }) {
return (
<ScreenshotsWrapper>
{screenshots.map((screenshot) => (
<ScreenshotWrapper key={screenshot.src}>
<Image src={screenshot} alt="" />
</ScreenshotWrapper>
))}
</ScreenshotsWrapper>
);
}
function Display({ game }: { game: ShowcaseGame }) { function Display({ game }: { game: ShowcaseGame }) {
return ( return (
<> <>

View file

@ -1,19 +1,35 @@
"use client"; "use client";
import { styled } from "styled-components"; import { styled } from "styled-components";
import Image, { StaticImageData } from "next/image";
export const ScreenshotsWrapper = styled.div` export function Screenshots({
screenshots,
}: {
screenshots: StaticImageData[];
}) {
return (
<ScreenshotsWrapper>
{screenshots.map((screenshot) => (
<Screenshot src={screenshot} alt="" key={screenshot.src} />
))}
</ScreenshotsWrapper>
);
}
const ScreenshotsWrapper = styled.div`
flex: 4; flex: 4;
text-align: center;
`; `;
export const ScreenshotWrapper = styled.div` const Screenshot = styled(Image)`
text-align: center; width: 100%;
img { width: max(
width: 100%; round(down, 100%, calc(240 * var(--device-pixel))),
width: round(down, 100%, 240px); calc(240 * var(--device-pixel))
height: auto; );
image-rendering: pixelated; height: auto;
} image-rendering: pixelated;
`; `;
export const Description = styled.div` export const Description = styled.div`
@ -26,6 +42,10 @@ export const Description = styled.div`
export const DescriptionAndScreenshots = styled.div` export const DescriptionAndScreenshots = styled.div`
display: flex; display: flex;
gap: 16px; gap: 16px;
@media (max-width: 1000px) {
display: block;
}
`; `;
export const BackToShowcaseWrapper = styled.div` export const BackToShowcaseWrapper = styled.div`

View file

@ -6,20 +6,23 @@ import Image from "next/image";
export const GameGrid = styled.div` export const GameGrid = styled.div`
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, 600px); grid-template-columns: repeat(auto-fit, min(100vw, 600px));
justify-content: center; justify-content: center;
gap: 48px; gap: 48px;
`; `;
export const GameImage = styled(Image)` export const GameImage = styled(Image)`
width: 100%; width: 100%;
width: round(down, 100%, 240px); width: max(
round(down, 100%, calc(240 * var(--device-pixel))),
min(calc(240 * var(--device-pixel)), 100vw)
);
height: auto; height: auto;
image-rendering: pixelated; image-rendering: pixelated;
`; `;
export const GameDisplay = styled(Link)` export const GameDisplay = styled(Link)`
width: 600px; width: 100%;
text-align: center; text-align: center;
color: black; color: black;
text-decoration: none; text-decoration: none;

View file

@ -14,11 +14,8 @@ const Section = styled.section<{ $color: string }>`
const CENTERED_CSS = ` const CENTERED_CSS = `
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
max-width: 60%; width: 60%;
min-width: min(95%, 1000px);
@media (max-width: 40rem) {
max-width: 90%;
}
`; `;
export const CenteredBlock = styled.div` export const CenteredBlock = styled.div`