Skip to content

Commit

Permalink
Merge pull request #418 from pekq/feat/add-mask-border
Browse files Browse the repository at this point in the history
feat: change mask border color
  • Loading branch information
mosch authored Dec 10, 2024
2 parents 84f4b4b + dea2658 commit ec059fe
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export default MyEditor
| border | Number\|Number[] | The cropping border. Image will be visible through the border, but cut off in the resulting image. Treated as horizontal and vertical borders when passed an array. |
| borderRadius | Number | The cropping area border radius. |
| color | Number[] | The color of the cropping border, in the form: [red (0-255), green (0-255), blue (0-255), alpha (0.0-1.0)]. |
| borderColor | Number[] | The color of the 1px border around the mask, in the form: [red (0-255), green (0-255), blue (0-255), alpha (0.0-1.0)]. If not provided, no border will be drawn. |
| backgroundColor | String | The background color of the image if it's transparent. |
| style | Object | Styles for the canvas element. |
| scale | Number | The scale of the image. You can use this to add your own resizing slider. |
Expand Down
38 changes: 37 additions & 1 deletion packages/demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type State = {
isTransparent: boolean
backgroundColor?: string
showGrid: boolean
borderColor: string
}

const App = () => {
Expand All @@ -49,6 +50,7 @@ const App = () => {
isTransparent: false,
backgroundColor: undefined,
showGrid: false,
borderColor: '#ffffff80', // Default border color (white with 50% opacity)
})

const handleNewImage = (e: ChangeEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -153,6 +155,10 @@ const App = () => {
const handleShowGrid = (e: ChangeEvent<HTMLInputElement>) =>
setState({ ...state, showGrid: e.target.checked })

const handleBorderColorChange = (e: ChangeEvent<HTMLInputElement>) => {
setState({ ...state, borderColor: e.target.value })
}

return (
<div>
<Dropzone
Expand All @@ -178,6 +184,7 @@ const App = () => {
onImageReady={logCallback.bind(this, 'onImageReady')}
image={state.image}
disableCanvasRotation={state.disableCanvasRotation}
borderColor={hexToRgba(state.borderColor)}
/>
<input
name="newImage"
Expand Down Expand Up @@ -292,6 +299,26 @@ const App = () => {
</div>
)}
<br />
Border Color:
<input
name="borderColor"
type="color"
value={state.borderColor.slice(0, 7)}
onChange={handleBorderColorChange}
/>
Opacity:
<input
name="borderOpacity"
type="range"
min="0"
max="255"
value={parseInt(state.borderColor.slice(7, 9), 16)}
onChange={(e) => {
const opacity = parseInt(e.target.value).toString(16).padStart(2, '0')
setState({ ...state, borderColor: state.borderColor.slice(0, 7) + opacity })
}}
/>
<br />
<input type="button" onClick={handleSave} value="Preview" />
<br />
{state.preview && (
Expand Down Expand Up @@ -322,4 +349,13 @@ const App = () => {
)
}

export default App
// Helper function to convert hex color with alpha to RGBA array
function hexToRgba(hex: string): [number, number, number, number] {
const r = parseInt(hex.slice(1, 3), 16)
const g = parseInt(hex.slice(3, 5), 16)
const b = parseInt(hex.slice(5, 7), 16)
const a = hex.length === 9 ? parseInt(hex.slice(7, 9), 16) / 255 : 1
return [r, g, b, a]
}

export default App
19 changes: 19 additions & 0 deletions packages/lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const drawRoundedRect = (
Math.PI * 0.5,
Math.PI,
)
context.closePath()
context.translate(-x, -y)
}
}
Expand Down Expand Up @@ -126,6 +127,7 @@ export interface Props {
disableBoundaryChecks?: boolean
disableHiDPIScaling?: boolean
disableCanvasRotation?: boolean
borderColor?: [number, number, number, number?]
}

export interface Position {
Expand Down Expand Up @@ -592,6 +594,22 @@ class AvatarEditor extends React.Component<PropsWithDefaults, State> {
context.rect(width, 0, -width, height) // outer rect, drawn "counterclockwise"
context.fill('evenodd')

// Draw 1px border around the mask only if borderColor is provided
if (this.props.borderColor) {
context.strokeStyle = 'rgba(' + this.props.borderColor.slice(0, 4).join(',') + ')'
context.lineWidth = 1
context.beginPath()
drawRoundedRect(
context,
borderSizeX + 0.5,
borderSizeY + 0.5,
width - borderSizeX * 2 - 1,
height - borderSizeY * 2 - 1,
borderRadius,
)
context.stroke()
}

if (this.props.showGrid) {
drawGrid(
context,
Expand Down Expand Up @@ -713,6 +731,7 @@ class AvatarEditor extends React.Component<PropsWithDefaults, State> {
disableCanvasRotation,
showGrid,
gridColor,
borderColor,
...rest
} = this.props

Expand Down

0 comments on commit ec059fe

Please sign in to comment.