UNPKG

@npio/internals

Version:

A free visual website editor, powered with your own SolidJS components.

128 lines (112 loc) 2.78 kB
import { Sharp } from "sharp"; import { clamp } from "../../number"; import { getImageOptimizationTarget, getProportionalCropTarget, } from "../../string"; export type Dimensions = { width: number; height: number; }; export type Region = Dimensions & { left: number; top: number; }; type FocalPoint = { x: number; y: number }; // FocalPoint cropping based on https://github.com/lovell/sharp/issues/2740#issuecomment-1279560949 export const crop = async function (params: { sharp: Sharp; size: number; aspectRatio?: string; dpr?: number; focalPoint?: FocalPoint; }) { const { sharp, size, aspectRatio, dpr } = params; const metadata = await sharp.metadata(); if (metadata.width == null || metadata.height == null) { throw new Error("Cannot resize image without dimensions"); } const original = { width: metadata.width, height: metadata.height, }; const rawTarget = getImageOptimizationTarget({ original, size, aspectRatio, dpr, }); const target = getProportionalCropTarget(original, rawTarget); const focalPoint = params.focalPoint || { x: 0.5, y: 0.5 }; const { width, height, factor } = getResizeOptions({ original, target, }); const region = getExtractRegion({ focalPoint: { x: Math.round((original.width * focalPoint.x) / factor), y: Math.round((original.height * focalPoint.y) / factor), }, target, intermediate: { width, height, }, }); return sharp .rotate() .resize({ fit: "cover", width, height, withoutEnlargement: true, }) .extract(region); }; const getResizeOptions = ({ original, target, }: { original: Dimensions; target: Dimensions; }): { width: number; height: number; factor: number } => { const heightRatio = original.height / target.height; const widthRatio = original.width / target.width; let factor: number; let width: number; let height: number; if (heightRatio < widthRatio) { factor = heightRatio; height = Math.round(target.height); width = Math.round(original.width / factor); } else { factor = widthRatio; width = Math.round(target.width); height = Math.round(original.height / factor); } return { width, height, factor }; }; const getExtractRegion = ({ focalPoint, target, intermediate, }: { focalPoint: FocalPoint; target: Dimensions; intermediate: Dimensions; }): Region => { return { left: clamp( Math.round(focalPoint.x - target.width / 2), 0, intermediate.width - target.width, ), top: clamp( Math.round(focalPoint.y - target.height / 2), 0, intermediate.height - target.height, ), width: target.width, height: target.height, }; };