Fix exporting of cropped images. (#2268)
Open this PR in different browsers, and you will see that the svg below will render differently in different browsers. The svg was exported from our staging. Seems like Chrome handles `clip-path` when set via the style differently than Safari and Firefox. I've reworked the logic so that it now uses a `clip-path` definition and applies that to the image. ![shape_xPSLLIG9yQkqAACrv1OxE](https://github.com/tldraw/tldraw/assets/2523721/4d0baba3-f5bf-4e78-96fe-aaa91ead107f) Also fixes a bunch of issues when copy pasting via the menu. It seems like we can't store the write function to a variable: ![image](https://github.com/tldraw/tldraw/assets/2523721/8d38edaf-8d63-462b-9f1a-c38960add7d7) Fixes https://github.com/tldraw/tldraw/issues/2254 ### Before ![CleanShot 2023-11-30 at 09 30 24](https://github.com/tldraw/tldraw/assets/2523721/93c06d5f-bfb2-4d97-8819-a8560c770304) ### After ![CleanShot 2023-11-30 at 09 30 55](https://github.com/tldraw/tldraw/assets/2523721/29e0f699-d0e8-4fb2-b90e-1d14ee609e52) ### Change Type - [x] `patch` — Bug fix - [ ] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [ ] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan 1. Add an image, apply crop to it (best if you crop from all sides, just to make sure) 2. Copy as png and make sure the image is correctly cropped in the created image. ### Release Notes - Fix exporting of cropped images.
This commit is contained in:
parent
6f59e54da6
commit
dcf2ad9820
2 changed files with 45 additions and 29 deletions
|
@ -186,14 +186,29 @@ export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
|
|||
const crop = shape.props.crop
|
||||
if (containerStyle.transform && crop) {
|
||||
const { transform, width, height } = containerStyle
|
||||
const croppedWidth = (crop.bottomRight.x - crop.topLeft.x) * width
|
||||
const croppedHeight = (crop.bottomRight.y - crop.topLeft.y) * height
|
||||
|
||||
const points = [
|
||||
new Vec2d(crop.topLeft.x * width, crop.topLeft.y * height),
|
||||
new Vec2d(crop.bottomRight.x * width, crop.topLeft.y * height),
|
||||
new Vec2d(crop.bottomRight.x * width, crop.bottomRight.y * height),
|
||||
new Vec2d(crop.topLeft.x * width, crop.bottomRight.y * height),
|
||||
new Vec2d(0, 0),
|
||||
new Vec2d(croppedWidth, 0),
|
||||
new Vec2d(croppedWidth, croppedHeight),
|
||||
new Vec2d(0, croppedHeight),
|
||||
]
|
||||
|
||||
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon')
|
||||
polygon.setAttribute('points', points.map((p) => `${p.x},${p.y}`).join(' '))
|
||||
|
||||
const clipPath = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath')
|
||||
clipPath.setAttribute('id', 'cropClipPath')
|
||||
clipPath.appendChild(polygon)
|
||||
|
||||
const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')
|
||||
defs.appendChild(clipPath)
|
||||
g.appendChild(defs)
|
||||
|
||||
const innerElement = document.createElementNS('http://www.w3.org/2000/svg', 'g')
|
||||
innerElement.style.clipPath = `polygon(${points.map((p) => `${p.x}px ${p.y}px`).join(',')})`
|
||||
innerElement.setAttribute('clip-path', 'url(#cropClipPath)')
|
||||
image.setAttribute('width', width.toString())
|
||||
image.setAttribute('height', height.toString())
|
||||
image.style.transform = transform
|
||||
|
|
|
@ -23,7 +23,6 @@ export function copyAs(
|
|||
// Note: it's important that this function itself isn't async - we need to create the relevant
|
||||
// `ClipboardItem`s synchronously to make sure safari knows that the user _wants_ to copy
|
||||
// See https://bugs.webkit.org/show_bug.cgi?id=222262
|
||||
const write = window.navigator.clipboard?.write
|
||||
|
||||
return editor
|
||||
.getSvg(ids?.length ? ids : [...editor.getCurrentPageShapeIds()], {
|
||||
|
@ -39,8 +38,8 @@ export function copyAs(
|
|||
switch (format) {
|
||||
case 'svg': {
|
||||
if (window.navigator.clipboard) {
|
||||
if (write) {
|
||||
write([
|
||||
if (window.navigator.clipboard.write) {
|
||||
window.navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
'text/plain': new Blob([getSvgAsString(svg)], { type: 'text/plain' }),
|
||||
}),
|
||||
|
@ -71,27 +70,29 @@ export function copyAs(
|
|||
})
|
||||
|
||||
const mimeType = format === 'jpeg' ? 'image/jpeg' : 'image/png'
|
||||
if (write) {
|
||||
write([
|
||||
new ClipboardItem({
|
||||
[mimeType]: blobPromise,
|
||||
}),
|
||||
]).catch((err: any) => {
|
||||
// Firefox will fail with the above if `dom.events.asyncClipboard.clipboardItem` is enabled.
|
||||
// See <https://github.com/tldraw/tldraw/issues/1325>
|
||||
if (!err.toString().match(/^TypeError: DOMString not supported/)) {
|
||||
console.error(err)
|
||||
}
|
||||
if (window.navigator.clipboard.write) {
|
||||
window.navigator.clipboard
|
||||
.write([
|
||||
new ClipboardItem({
|
||||
[mimeType]: blobPromise,
|
||||
}),
|
||||
])
|
||||
.catch((err: any) => {
|
||||
// Firefox will fail with the above if `dom.events.asyncClipboard.clipboardItem` is enabled.
|
||||
// See <https://github.com/tldraw/tldraw/issues/1325>
|
||||
if (!err.toString().match(/^TypeError: DOMString not supported/)) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
blobPromise.then((blob) => {
|
||||
window.navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
// Note: This needs to use the promise based approach for safari/ios to not bail on a permissions error.
|
||||
[mimeType]: blob,
|
||||
}),
|
||||
])
|
||||
blobPromise.then((blob) => {
|
||||
window.navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
// Note: This needs to use the promise based approach for safari/ios to not bail on a permissions error.
|
||||
[mimeType]: blob,
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
break
|
||||
|
@ -101,8 +102,8 @@ export function copyAs(
|
|||
const data = editor.getContentFromCurrentPage(ids)
|
||||
const jsonStr = JSON.stringify(data)
|
||||
|
||||
if (write) {
|
||||
write([
|
||||
if (window.navigator.clipboard.write) {
|
||||
window.navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
'text/plain': new Blob([jsonStr], { type: 'text/plain' }),
|
||||
}),
|
||||
|
|
Loading…
Reference in a new issue