@codedazur/react-parallax
Advanced tools
Comparing version 0.0.2 to 0.1.0
# @codedazur/react-parallax | ||
## 0.1.0 | ||
### Minor Changes | ||
- [#202](https://github.com/codedazur/toolkit/pull/202) [`43e902b`](https://github.com/codedazur/toolkit/commit/43e902bce11ffe819b2719c2f66323567baa6720) Thanks [@thijsdaniels](https://github.com/thijsdaniels)! - support scaling and offset | ||
## 0.0.2 | ||
@@ -7,3 +13,3 @@ | ||
- 9cccd57: add react essentials as dep | ||
- b27ce3a: add react essentials as dep | ||
@@ -10,0 +16,0 @@ ## 0.0.1 |
import { Vector2 } from '@codedazur/essentials'; | ||
import { MaybeRef } from '@codedazur/react-essentials'; | ||
import { RefObject } from 'react'; | ||
type ParallaxFactor = number | ((position: Vector2) => Vector2); | ||
declare function useParallax(parameters: { | ||
interface BaseUseParallaxProps { | ||
scrollRef?: MaybeRef<HTMLElement>; | ||
factor: ParallaxFactor; | ||
}): Vector2; | ||
declare function useParallax(parameters: { | ||
scrollRef?: MaybeRef<HTMLElement>; | ||
factor: ParallaxFactor[]; | ||
}): Vector2[]; | ||
cover?: boolean; | ||
} | ||
interface CoverUseParallaxProps extends BaseUseParallaxProps { | ||
factor: PrimitiveParallaxFactor; | ||
cover?: boolean; | ||
} | ||
interface CustomUseParallaxProps extends BaseUseParallaxProps { | ||
factor: ParallaxFactorFunction; | ||
cover?: false; | ||
} | ||
type UseParallaxProps = CoverUseParallaxProps | CustomUseParallaxProps; | ||
type PrimitiveParallaxFactor = number | Vector2; | ||
type ParallaxFactorFunction = (position: Vector2) => Vector2; | ||
type ParallaxFactor = PrimitiveParallaxFactor | ParallaxFactorFunction; | ||
/** | ||
* Creates a ref that can be used to apply a parallax effect to an element. | ||
* | ||
* @param factor The factor of the parallax effect. A number between -1 and 1. | ||
* @param scrollRef The scrollable element. Defaults to the window. | ||
* @returns A ref to the element. | ||
* | ||
* @example | ||
* const ref = useParallax<HTMLDivElement>({ | ||
* factor: 0.5, | ||
* }); | ||
* | ||
* return <div ref={ref} /> | ||
* | ||
* @todo A bug currently causes the element to jump on the first scroll event | ||
* when `cover` is set to true, because the initial position is not calculated. | ||
* This can be fixed by calling the `onScroll` callback of the `useScroll` hook | ||
* on the initial render to set the initial position. | ||
*/ | ||
declare function useParallax<T extends HTMLElement>({ scrollRef, factor, cover, }: UseParallaxProps): RefObject<T>; | ||
export { ParallaxFactor, useParallax }; | ||
export { UseParallaxProps, useParallax }; |
@@ -33,14 +33,36 @@ "use strict"; | ||
scrollRef, | ||
factor | ||
factor, | ||
cover = false | ||
}) { | ||
const [translation, setTranslation] = (0, import_react.useState)( | ||
Array.isArray(factor) ? factor.map(() => import_essentials.Vector2.zero) : import_essentials.Vector2.zero | ||
); | ||
const ref = (0, import_react.useRef)(null); | ||
const position = (0, import_react.useRef)(import_essentials.Vector2.zero); | ||
const offset = (0, import_react.useRef)(import_essentials.Vector2.zero); | ||
const windowSize = (0, import_react.useRef)(import_essentials.Vector2.zero); | ||
const elementSize = (0, import_react.useRef)(import_essentials.Vector2.zero); | ||
const translation = (0, import_react.useRef)(import_essentials.Vector2.zero); | ||
const scale = (0, import_react.useRef)(1); | ||
const applyTransform = (0, import_react.useCallback)(() => { | ||
const target = (0, import_react_essentials.resolveMaybeRef)(ref); | ||
if (!target) { | ||
return; | ||
} | ||
window.requestAnimationFrame(() => { | ||
target.style.transform = `translate3d(${translation.current.x}px, ${translation.current.y}px, 0) scale(${scale.current})`; | ||
}); | ||
}, []); | ||
const handleScroll = (0, import_react.useCallback)( | ||
({ position }) => { | ||
setTranslation( | ||
Array.isArray(factor) ? factor.map((factor2) => translate(position, factor2)) : translate(position, factor) | ||
(state) => { | ||
position.current = state.position; | ||
if (cover === true && ref.current) { | ||
const scrollOffset = getOffset(ref.current).subtract( | ||
translation.current | ||
); | ||
offset.current = scrollOffset.subtract(windowSize.current.divide(2)).add(elementSize.current.divide(2)).multiply(import_essentials.Vector2.one.subtract(factor)).multiply(Direction.up); | ||
} | ||
translation.current = translate(position.current, factor).add( | ||
offset.current | ||
); | ||
applyTransform(); | ||
}, | ||
[factor] | ||
[applyTransform, cover, factor] | ||
); | ||
@@ -51,7 +73,96 @@ (0, import_react_essentials.useScroll)({ | ||
}); | ||
return translation; | ||
const setScale = (0, import_react.useCallback)(() => { | ||
if (!cover) { | ||
return; | ||
} | ||
scale.current = Math.max( | ||
windowSize.current.x / elementSize.current.x, | ||
windowSize.current.y / elementSize.current.y | ||
); | ||
applyTransform(); | ||
}, [applyTransform, cover]); | ||
useSize({ | ||
ref, | ||
onResize: (size) => { | ||
elementSize.current = size; | ||
setScale(); | ||
} | ||
}); | ||
useWindowSize({ | ||
onResize: (size) => { | ||
windowSize.current = size; | ||
setScale(); | ||
} | ||
}); | ||
return ref; | ||
} | ||
function getOffset(element) { | ||
const rectangle = element.getBoundingClientRect(); | ||
const scrollX = document.documentElement.scrollLeft; | ||
const scrollY = document.documentElement.scrollTop; | ||
const clientX = document.documentElement.clientLeft || 0; | ||
const clientY = document.documentElement.clientTop || 0; | ||
const x = rectangle.x + scrollX - clientX; | ||
const y = rectangle.y + scrollY - clientY; | ||
return new import_essentials.Vector2(Math.round(x), Math.round(y)); | ||
} | ||
function translate(position, factor) { | ||
return factor instanceof Function ? factor(position) : position.multiply(1 - factor); | ||
return factor instanceof Function ? factor(position) : position.multiply(import_essentials.Vector2.one.subtract(factor)); | ||
} | ||
function useSize({ | ||
ref, | ||
onResize | ||
}) { | ||
const observer = (0, import_react.useRef)(null); | ||
const handleResize = (0, import_react.useCallback)( | ||
(entries) => { | ||
if (entries.length > 0) { | ||
const { width, height } = entries[0].contentRect; | ||
onResize(new import_essentials.Vector2(width, height)); | ||
} | ||
}, | ||
[onResize] | ||
); | ||
(0, import_react.useEffect)(() => { | ||
const target = (0, import_react_essentials.resolveMaybeRef)(ref != null ? ref : document.body); | ||
if (!target) { | ||
return; | ||
} | ||
observer.current = new ResizeObserver(handleResize); | ||
observer.current.observe(target); | ||
return () => { | ||
if (observer.current) { | ||
observer.current.disconnect(); | ||
} | ||
}; | ||
}, [handleResize, ref]); | ||
(0, import_react.useEffect)(() => { | ||
const target = (0, import_react_essentials.resolveMaybeRef)(ref != null ? ref : document.body); | ||
if (!target) { | ||
return; | ||
} | ||
const { width, height } = target.getBoundingClientRect(); | ||
onResize(new import_essentials.Vector2(width, height)); | ||
}, [onResize, ref]); | ||
} | ||
function useWindowSize({ | ||
onResize | ||
}) { | ||
const handleResize = (0, import_react.useCallback)(() => { | ||
onResize(new import_essentials.Vector2(window.innerWidth, window.innerHeight)); | ||
}, [onResize]); | ||
(0, import_react.useEffect)(() => { | ||
handleResize(); | ||
window.addEventListener("resize", handleResize); | ||
return () => { | ||
window.removeEventListener("resize", handleResize); | ||
}; | ||
}, [handleResize]); | ||
} | ||
var Direction = { | ||
up: new import_essentials.Vector2(0, -1), | ||
down: new import_essentials.Vector2(0, 1), | ||
left: new import_essentials.Vector2(-1, 0), | ||
right: new import_essentials.Vector2(1, 0) | ||
}; | ||
// Annotate the CommonJS export names for ESM import in node: | ||
@@ -58,0 +169,0 @@ 0 && (module.exports = { |
{ | ||
"name": "@codedazur/react-parallax", | ||
"version": "0.0.2", | ||
"version": "0.1.0", | ||
"main": ".dist/index.js", | ||
@@ -13,2 +13,5 @@ "module": "./dist/index.mjs", | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"license": "MIT", | ||
@@ -18,4 +21,4 @@ "scripts": { | ||
"test": "echo \"No tests configured.\"", | ||
"release": "echo \"No releases configured.\"", | ||
"build": "tsup index.ts --format esm,cjs --dts" | ||
"build": "tsup index.ts --format esm,cjs --dts", | ||
"dev": "tsup index.ts --format esm,cjs --dts --watch --external react" | ||
}, | ||
@@ -30,10 +33,10 @@ "dependencies": { | ||
"devDependencies": { | ||
"@types/react": "^18.0.24", | ||
"@types/react-dom": "^18.0.8", | ||
"eslint": "^8.26.0", | ||
"eslint-config-custom": "*", | ||
"@types/react": "^18.2.18", | ||
"@types/react-dom": "^18.2.7", | ||
"eslint": "^8.46.0", | ||
"@codedazur/eslint-config": "*", | ||
"react": "^18.2.0", | ||
"tsconfig": "*", | ||
"typescript": "^4.8.4" | ||
"@codedazur/tsconfig": "*", | ||
"typescript": "^5.1.6" | ||
} | ||
} |
{ | ||
"extends": "tsconfig/react-library.json", | ||
"extends": "@codedazur/tsconfig/react-library.json", | ||
"include": ["."], | ||
"exclude": ["dist", "build", "node_modules"] | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
24009
14
574
0
2