sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
136 lines (114 loc) • 2.96 kB
text/typescript
import {Observable} from 'rxjs'
/* eslint-disable */
/**
* Check if we need to change dims.
*/
function rotated(n: number) {
return [5, 6, 7, 8].indexOf(n) > -1
}
type RotateOpts = {
degrees?: number
x: number
y: number
}
// Based on github.com/component/rotate
function rotate(ctx: CanvasRenderingContext2D | null, options: RotateOpts) {
const x = options.x
const y = options.y
const radians = (options.degrees || 0) * (Math.PI / 180)
ctx?.translate(x, y)
ctx?.rotate(radians)
ctx?.translate(-x, -y)
}
// Based on github.com/component/flip
function flip(canvas: HTMLCanvasElement, x: boolean, y: boolean) {
const ctx = canvas.getContext('2d')
ctx?.translate(x ? canvas.width : 0, y ? canvas.height : 0)
ctx?.scale(x ? -1 : 1, y ? -1 : 1)
}
const ORIENTATION_OPS = [
{op: 'none', degrees: 0},
{op: 'flip-x', degrees: 0},
{
op: 'none',
degrees: 180,
},
{op: 'flip-y', degrees: 0},
{op: 'flip-x', degrees: 90},
{op: 'none', degrees: 90},
{
op: 'flip-x',
degrees: -90,
},
{op: 'none', degrees: -90},
]
export type Orientation =
| 'top-left'
| 'top-right'
| 'bottom-right'
| 'bottom-left'
| 'left-top'
| 'right-top'
| 'right-bottom'
| 'left-bottom'
const ORIENTATIONS: Array<Orientation> = [
'top-left',
'top-right',
'bottom-right',
'bottom-left',
'left-top',
'right-top',
'right-bottom',
'left-bottom',
]
export const DEFAULT_ORIENTATION: Orientation = 'top-left'
const THUMB_SIZE = 120
// Based on github.com/component/exif-rotate
function _orient(img: HTMLImageElement, orientationNumber: number): HTMLCanvasElement {
const orientation = ORIENTATION_OPS[orientationNumber - 1]
const ratio = img.height / img.width
img.width = THUMB_SIZE / ratio
img.height = img.width * ratio
// canvas
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
// dims
if (rotated(orientationNumber)) {
canvas.height = img.width
canvas.width = img.height
} else {
canvas.width = img.width
canvas.height = img.height
}
// flip
if (orientation.op === 'flip-x') {
flip(canvas, true, false)
}
if (orientation.op === 'flip-y') {
flip(canvas, false, true)
}
// rotate
if (orientation.degrees) {
rotate(ctx, {
degrees: orientation.degrees,
x: canvas.width / 2,
y: canvas.height / 2,
})
if (rotated(orientationNumber)) {
const d = canvas.width - canvas.height
ctx?.translate(d / 2, -d / 2)
}
}
ctx?.drawImage(img, 0, 0, img.width, img.height)
return canvas
}
/* eslint-enable */
export function orient(image: HTMLImageElement, orientationId: Orientation) {
return new Observable((observer) => {
// console.time('canvas to blob')
const orientation = ORIENTATIONS.indexOf(orientationId) + 1
const canvas = _orient(image, orientation)
observer.next(canvas.toDataURL('image/jpeg', 0.1))
observer.complete()
})
}