Skip to content

Commit a095f7d

Browse files
DennisSmolekclaude
andauthored
feat: many updates (#2643)
* fix: bundle requires browser output in platform (#2608) * fix: bundle requires browser output in platform * fix: rolldown issues * fix: finalize rollup * chore: prettier * chore: prettier * fix: adjust root name for better local linking. Dist folder package confused pnpm * fix: update lockfile for ci * fix: update tests for unbuild * fix: simplified canary * fix: package name copying * feat: cleaned up docs, please god trigger a push * feat: actual push, I hate git * feat: prettier changes * feat: proper file endings (#2615) * feat: add case-insensitive commit type handling to semantic-release The commit analyzer was rejecting commits with capitalized types (Feat, Fix) which caused releases to silently fail. This adds explicit release rules for common case variations to prevent future release failures. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * V11 general fixes (#2640) * fix: sandbox starter. closes Bug report template starter is broken Fixes #2553 * feat: minor fixes to dragControls and portal closes #2097 * fix: fisheye events closes #2165 * feat: Multi-type instances objects closes: #2181 * feat: Native Eclusions List * feat: PivotControls Enhanced. Added object selection from old demo version. Added Webgpu support for lines Addded story for object attach mode * feat: Add Blur listener for keyboard events if the wondow is exited * Fix: transforms in html closes #2506 * feat: Canvas Resize bugs with safari * feat: onHover for PivotControls * fix: Update types for segments. closes: #2561 * chore: clean vanilla from webgpu * Fix parallel plane in rotatior of PivotControls closes #2406 * feat: add interactive priority * fix: Scroll Controls React 19 issues. Closes #2431 * feat: use external ktx loader * feat: fix html feat: fix reflector being crazy * chore: fix types * chore: linter * chore: allow for alt versions of r3f * chore: update lockfile * fix: odd CI bug * feat: pixelPerfect html handling. (#2642) Solves subpixel bluring closes #2380 --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 4c00c31 commit a095f7d

4 files changed

Lines changed: 37 additions & 9 deletions

File tree

.claude/settings.local.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
"Bash(npx vitest:*)",
1919
"Bash(node test-debug.mjs:*)",
2020
"Bash(yarn test:bundles:*)",
21-
"Bash(test:*)"
21+
"Bash(test:*)",
22+
"Bash(yarn typecheck:*)",
23+
"Bash(yarn eslint:*)"
2224
]
2325
}
2426
}

src/core/Controls/ScrollControls/ScrollControls.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { context as fiberContext, RootState, useFrame, useThree } from '@react-t
55
import { DomEvent } from '@react-three/fiber/'
66
import { easing } from 'maath'
77
import { ForwardRefComponent } from '../../../utils/ts-utils'
8+
import { roundToPixelRatio } from '../../../utils/generic'
89

910
export type ScrollControlsProps = {
1011
/** Precision, default 0.00001 */
@@ -27,6 +28,8 @@ export type ScrollControlsProps = {
2728
/** If true attaches the scroll container before the canvas */
2829
prepend?: boolean
2930
enabled?: boolean
31+
/** Snaps translate3d values to physical device pixels to prevent subpixel blurriness, default true */
32+
pixelPerfect?: boolean
3033
style?: React.CSSProperties
3134
children: React.ReactNode
3235
}
@@ -44,6 +47,7 @@ export type ScrollControlsState = {
4447
range(from: number, distance: number, margin?: number): number
4548
curve(from: number, distance: number, margin?: number): number
4649
visible(from: number, distance: number, margin?: number): boolean
50+
pixelPerfect: boolean
4751
}
4852

4953
const context = /* @__PURE__ */ React.createContext<ScrollControlsState>(null!)
@@ -91,6 +95,7 @@ export function ScrollControls({
9195
damping = 0.25,
9296
maxSpeed = Infinity,
9397
prepend = false,
98+
pixelPerfect = true,
9499
style = {},
95100
children,
96101
}: ScrollControlsProps) {
@@ -113,6 +118,7 @@ export function ScrollControls({
113118
delta: 0,
114119
scroll,
115120
pages,
121+
pixelPerfect,
116122
// 0-1 for a range between from -> from + distance
117123
range(from: number, distance: number, margin: number = 0) {
118124
const start = from - margin
@@ -131,7 +137,7 @@ export function ScrollControls({
131137
},
132138
}
133139
return state
134-
}, [eps, damping, horizontal, pages])
140+
}, [eps, damping, horizontal, pages, pixelPerfect])
135141

136142
React.useEffect(() => {
137143
el.style.position = 'absolute'
@@ -275,9 +281,13 @@ const ScrollHtml: ForwardRefComponent<{ children?: React.ReactNode; style?: Reac
275281
}, [state.fixed])
276282
useFrame(() => {
277283
if (state.delta > state.eps) {
278-
group.current.style.transform = `translate3d(${
279-
state.horizontal ? -width * (state.pages - 1) * state.offset : 0
280-
}px,${state.horizontal ? 0 : height * (state.pages - 1) * -state.offset}px,0)`
284+
let x = state.horizontal ? -width * (state.pages - 1) * state.offset : 0
285+
let y = state.horizontal ? 0 : height * (state.pages - 1) * -state.offset
286+
if (state.pixelPerfect) {
287+
x = roundToPixelRatio(x)
288+
y = roundToPixelRatio(y)
289+
}
290+
group.current.style.transform = `translate3d(${x}px,${y}px,0)`
281291
}
282292
})
283293
root.render(

src/core/UI/Html/Html.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Assign } from 'utility-types'
1717
import { ThreeElements, useFrame, useThree } from '@react-three/fiber'
1818
import { ForwardRefComponent } from '../../../utils/ts-utils'
1919
import { roundEven } from '../../../utils/roundEven'
20+
import { roundToPixelRatio } from '../../../utils/generic'
2021

2122
const v1 = /* @__PURE__ */ new Vector3()
2223
const v2 = /* @__PURE__ */ new Vector3()
@@ -142,6 +143,8 @@ export interface HtmlProps extends Omit<Assign<React.HTMLAttributes<HTMLDivEleme
142143
geometry?: React.ReactNode // Material for occlusion plane
143144
castShadow?: boolean // Cast shadow for occlusion plane
144145
receiveShadow?: boolean // Receive shadow for occlusion plane
146+
/** Snaps translate3d values to physical device pixels to prevent subpixel blurriness, default true */
147+
pixelPerfect?: boolean
145148
}
146149

147150
/**
@@ -187,6 +190,7 @@ export const Html: ForwardRefComponent<HtmlProps, HTMLDivElement> = /* @__PURE__
187190
as = 'div',
188191
wrapperClass,
189192
pointerEvents = 'auto',
193+
pixelPerfect = true,
190194
...props
191195
}: HtmlProps,
192196
ref: React.Ref<HTMLDivElement>
@@ -369,8 +373,12 @@ export const Html: ForwardRefComponent<HtmlProps, HTMLDivElement> = /* @__PURE__
369373
el.style.zIndex = `${objectZIndex(group.current, camera, zRange)}`
370374

371375
if (transform) {
372-
const widthHalf = forceEven ? roundEven(size.width / 2) : size.width / 2
373-
const heightHalf = forceEven ? roundEven(size.height / 2) : size.height / 2
376+
let widthHalf = forceEven ? roundEven(size.width / 2) : size.width / 2
377+
let heightHalf = forceEven ? roundEven(size.height / 2) : size.height / 2
378+
if (pixelPerfect) {
379+
widthHalf = roundToPixelRatio(widthHalf)
380+
heightHalf = roundToPixelRatio(heightHalf)
381+
}
374382
const fov = camera.projectionMatrix.elements[5] * heightHalf
375383
const { isOrthographicCamera, top, left, bottom, right } = camera as OrthographicCamera
376384
const cameraMatrix = getCameraCSSMatrix(camera.matrixWorldInverse)
@@ -394,8 +402,12 @@ export const Html: ForwardRefComponent<HtmlProps, HTMLDivElement> = /* @__PURE__
394402
}
395403
} else {
396404
const scale = distanceFactor === undefined ? 1 : objectScale(group.current, camera) * distanceFactor
397-
const x = forceEven ? roundEven(vec[0]) : vec[0]
398-
const y = forceEven ? roundEven(vec[1]) : vec[1]
405+
let x = forceEven ? roundEven(vec[0]) : vec[0]
406+
let y = forceEven ? roundEven(vec[1]) : vec[1]
407+
if (pixelPerfect) {
408+
x = roundToPixelRatio(x)
409+
y = roundToPixelRatio(y)
410+
}
399411
el.style.transform = `translate3d(${x}px,${y}px,0) scale(${scale})`
400412
}
401413
oldPosition.current = vec

src/utils/generic.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export function roundToPixelRatio(value: number) {
2+
const ratio = window.devicePixelRatio || 1
3+
return Math.round(value * ratio) / ratio
4+
}

0 commit comments

Comments
 (0)