-
Notifications
You must be signed in to change notification settings - Fork 466
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #100 from Shopify/feature/offscreen-surface-snapshots
Added support for offscreen surface and image toByteArray/toBase64
- Loading branch information
Showing
72 changed files
with
2,358 additions
and
355 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
--- | ||
id: canvas | ||
title: Canvas | ||
sidebar_label: Overview | ||
slug: /canvas/overview | ||
--- | ||
|
||
The Canvas component is the root of your Skia drawing. | ||
You can treat it as a regular React Native view and assign a view style to it. | ||
Behind the scenes, it is using its own React renderer. | ||
|
||
| Name | Type | Description. | | ||
|:-----|:---------|:-----------------| | ||
| style | `ViewStyle` | View style. | | ||
| ref? | `Ref<SkiaView>` | Reference to the `SkiaView` object | | ||
| onTouch? | `TouchHandler` | Touch handler for the Canvas (see [touch handler](/docs/animations/overview#usetouchhandler)). | | ||
|
||
## Getting a Canvas Snapshot | ||
|
||
You can save your drawings as an image, using `makeImageSnapshot`. This method will return an [Image instance](/docs/images#instance-methods). This instance can be used to do anything: drawing it via the `<Image>` component, or being saved or shared using binary or base64 encoding. | ||
|
||
### Example | ||
|
||
```tsx twoslash | ||
import {useEffect} from "react"; | ||
import {Canvas, Image, useCanvasRef, Circle} from "@shopify/react-native-skia"; | ||
|
||
export const Demo = () => { | ||
const ref = useCanvasRef(); | ||
const onPress = useEffect(() => { | ||
setTimeout(() => { | ||
// you can pass an optional rectangle | ||
// to only save part of the image | ||
const image = ref.current?.makeImageSnapshot(); | ||
if (image) { | ||
// you can use image in an <Image> component | ||
// Or save to file using encodeToBytes -> Uint8Array | ||
const bytes = image.encodeToBytes(); | ||
} | ||
}, 1000) | ||
}); | ||
return ( | ||
<Canvas style={{ flex: 1 }} ref={ref}> | ||
<Circle r={128} cx={128} cy={128} color="red" /> | ||
</Canvas> | ||
); | ||
}; | ||
``` | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
example/src/Examples/Drawing/Context/functions/findClosestElementToPoint.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import type { Point } from "@shopify/react-native-skia"; | ||
|
||
import type { DrawingElements } from "../types"; | ||
|
||
import { getBounds } from "./getBounds"; | ||
|
||
export const findClosestElementToPoint = ( | ||
point: Point, | ||
elements: DrawingElements | ||
) => { | ||
// Empty elements returns undefined | ||
if (elements.length === 0) { | ||
return undefined; | ||
} | ||
// Check if we any of the paths (in reverse top-down order) contains the point | ||
for (let i = elements.length - 1; i >= 0; i--) { | ||
if (elements[i].path.contains(point.x, point.y)) { | ||
return elements[i]; | ||
} | ||
} | ||
// If not, measure distance to the closest path | ||
const distances = elements | ||
.map((element) => { | ||
const rect = getBounds(element); | ||
// check if point is in rect | ||
if ( | ||
point.x >= rect.x - 10 && | ||
point.x < rect.x + rect.width + 10 && | ||
point.y >= rect.y - 10 && | ||
point.y < rect.y + rect.height + 10 | ||
) { | ||
// Find distance from click to center of element | ||
var dx = Math.max(rect.x - point.x, point.x - (rect.x + rect.width)); | ||
var dy = Math.max(rect.y - point.y, point.y - (rect.y + rect.height)); | ||
return { ...element, distance: Math.sqrt(dx * dx + dy * dy) }; | ||
} else { | ||
return { ...element, distance: Number.MAX_VALUE }; | ||
} | ||
}) | ||
.sort((a, b) => a.distance - b.distance); | ||
|
||
return elements.find( | ||
(el) => | ||
el.path === distances[0].path && distances[0].distance < Number.MAX_VALUE | ||
); | ||
}; |
34 changes: 34 additions & 0 deletions
34
example/src/Examples/Drawing/Context/functions/findElementsInRect.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import type { IRect } from "@shopify/react-native-skia"; | ||
|
||
import type { DrawingElements } from "../types"; | ||
|
||
import { getBounds } from "./getBounds"; | ||
|
||
export const findElementsInRect = ( | ||
rect: IRect, | ||
elements: DrawingElements | ||
): DrawingElements | undefined => { | ||
const retVal: DrawingElements = []; | ||
const normalizedRect = { | ||
x: rect.width < 0 ? rect.x + rect.width : rect.x, | ||
y: rect.height < 0 ? rect.y + rect.height : rect.y, | ||
width: Math.abs(rect.width), | ||
height: Math.abs(rect.height), | ||
}; | ||
elements.forEach((element) => { | ||
const bounds = getBounds(element); | ||
if ( | ||
bounds.x >= normalizedRect.x && | ||
bounds.x + bounds.width <= normalizedRect.x + normalizedRect.width && | ||
bounds.y >= normalizedRect.y && | ||
bounds.y + bounds.height <= normalizedRect.y + normalizedRect.height | ||
) { | ||
retVal.push(element); | ||
} | ||
}); | ||
|
||
if (retVal.length > 0) { | ||
return retVal; | ||
} | ||
return undefined; | ||
}; |
48 changes: 48 additions & 0 deletions
48
example/src/Examples/Drawing/Context/functions/findResizeMode.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import type { Point } from "@shopify/react-native-skia"; | ||
|
||
import type { DrawingElements, ResizeMode } from "../types"; | ||
|
||
import { getBoundingBox } from "./getBoundingBox"; | ||
|
||
const hitSlop = 8; | ||
|
||
export const findResizeMode = ( | ||
point: Point, | ||
selectedElements: DrawingElements | ||
): ResizeMode | undefined => { | ||
const bounds = getBoundingBox(selectedElements); | ||
if (!bounds) { | ||
return undefined; | ||
} | ||
|
||
if ( | ||
point.x >= bounds.x - hitSlop && | ||
point.x <= bounds.x + hitSlop && | ||
point.y >= bounds.y - hitSlop && | ||
point.y <= bounds.y + hitSlop | ||
) { | ||
return "topLeft"; | ||
} else if ( | ||
point.x >= bounds.x + bounds.width - hitSlop && | ||
point.x <= bounds.x + bounds.width + hitSlop && | ||
point.y >= bounds.y - hitSlop && | ||
point.y <= bounds.y + hitSlop | ||
) { | ||
return "topRight"; | ||
} else if ( | ||
point.x >= bounds.x + bounds.width - hitSlop && | ||
point.x <= bounds.x + bounds.width + hitSlop && | ||
point.y >= bounds.y + bounds.height - hitSlop && | ||
point.y <= bounds.y + bounds.height + hitSlop | ||
) { | ||
return "bottomRight"; | ||
} else if ( | ||
point.x >= bounds.x - hitSlop && | ||
point.x <= bounds.x + hitSlop && | ||
point.y >= bounds.y + bounds.height - hitSlop && | ||
point.y <= bounds.y + bounds.height + hitSlop | ||
) { | ||
return "bottomLeft"; | ||
} | ||
return undefined; | ||
}; |
36 changes: 36 additions & 0 deletions
36
example/src/Examples/Drawing/Context/functions/getBoundingBox.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import type { DrawingElements } from "../types"; | ||
|
||
import { getBounds } from "./getBounds"; | ||
|
||
export const getBoundingBox = (elements: DrawingElements) => { | ||
if (elements.length === 0) { | ||
return undefined; | ||
} | ||
|
||
const bb = { | ||
x: Number.MAX_VALUE, | ||
y: Number.MAX_VALUE, | ||
right: Number.MIN_VALUE, | ||
bottom: Number.MIN_VALUE, | ||
}; | ||
|
||
for (let i = 0; i < elements.length; i++) { | ||
const element = elements[i]; | ||
const bounds = getBounds(element); | ||
|
||
if (bounds.x < bb.x) { | ||
bb.x = bounds.x; | ||
} | ||
if (bounds.y < bb.y) { | ||
bb.y = bounds.y; | ||
} | ||
if (bounds.x + bounds.width > bb.right) { | ||
bb.right = bounds.x + bounds.width; | ||
} | ||
if (bounds.y + bounds.height > bb.bottom) { | ||
bb.bottom = bounds.y + bounds.height; | ||
} | ||
} | ||
|
||
return { x: bb.x, y: bb.y, width: bb.right - bb.x, height: bb.bottom - bb.y }; | ||
}; |
Oops, something went wrong.