Untitled

 avatar
unknown
plain_text
a year ago
3.7 kB
5
Indexable
const hasPositionChanged = ({ pos, prevPos }) => pos !== prevPos

const valueInRange = ({ minScale, maxScale, scale }) => scale <= maxScale && scale >= minScale

// Function to calculate a new translate value based on the current and previous positions and the scale
const getTranslate =
	({ minScale, maxScale, scale }) =>
	({ pos, prevPos, translate }) =>
		valueInRange({ minScale, maxScale, scale }) && hasPositionChanged({ pos, prevPos })
			? translate + (pos - prevPos * scale) * (1 - 1 / scale)
			: translate

// Function to construct a CSS transform matrix string
const getMatrix = ({ scale, translateX, translateY }: Transform) =>
	`matrix(${scale}, 0, 0, ${scale}, ${translateX}, ${translateY})`

// Function to calculate a new scale value
const getScale = ({ scale, minScale, maxScale, scaleSensitivity, deltaScale }) => {
	let newScale = scale + deltaScale / (scaleSensitivity / scale)
	newScale = Math.max(minScale, Math.min(newScale, maxScale))
	return [scale, newScale]
}

// Function to update the transformation's translation values and apply the new transformation to the element
const pan = ({ state, originX, originY }) => {
	state.transformation.translateX += originX
	state.transformation.translateY += originY
	state.element.style.transform = getMatrix({
		scale: state.transformation.scale,
		translateX: state.transformation.translateX,
		translateY: state.transformation.translateY
	})
}

// Function to return an object with methods for panning the element
const canPan = (state) => ({
	panBy: ({ originX, originY }) => pan({ state, originX, originY }),
	panTo: ({ originX, originY, scale }) => {
		state.transformation.scale = scale
		pan({
			state,
			originX: originX - state.transformation.translateX,
			originY: originY - state.transformation.translateY
		})
	}
})

// Function to return an object with a method for zooming the element
const canZoom = (state) => ({
	zoom: ({ x, y, deltaScale }) => {
		// Calculate new scale and translation values based on the mouse position and delta scale
		// Update the element's transform origin and style with the new values
		// Update the state with the new transformation values
		const { left, top } = state.element.getBoundingClientRect()
		const { minScale, maxScale, scaleSensitivity } = state
		const [scale, newScale] = getScale({
			scale: state.transformation.scale,
			deltaScale,
			minScale,
			maxScale,
			scaleSensitivity
		})
		const originX = x - left
		const originY = y - top
		const newOriginX = originX / scale
		const newOriginY = originY / scale
		const translate = getTranslate({ scale, minScale, maxScale })
		const translateX = translate({
			pos: originX,
			prevPos: state.transformation.originX,
			translate: state.transformation.translateX
		})
		const translateY = translate({
			pos: originY,
			prevPos: state.transformation.originY,
			translate: state.transformation.translateY
		})

		state.element.style.transformOrigin = `${newOriginX}px ${newOriginY}px`
		state.element.style.transform = getMatrix({ scale: newScale, translateX, translateY })
		state.transformation = {
			originX: newOriginX,
			originY: newOriginY,
			translateX,
			translateY,
			scale: newScale
		}
	}
})

// Function to initialize the state and return an object combining panning and zooming capabilities
export const renderer = ({ minScale, maxScale, element, scaleSensitivity = 10 }) => {
	const state: State = {
		element,
		minScale,
		maxScale,
		scaleSensitivity,
		transformation: {
			originX: 0,
			originY: 0,
			translateX: 0,
			translateY: 0,
			scale: 1
		}
	}
	return Object.assign(canZoom(state), canPan(state))
}
Editor is loading...
Leave a Comment