Read the original article:Allowing Canvas Drawing Beyond Parent Component Boundaries
Context
In HarmonyOS development, developers may want to allow a Canvas component to render content beyond the bounds of its parent container. However, they often need to restrict the touch response area to remain within the parent’s limits. A common question arises: can the .clip property achieve this effect?
Description
- The
.clipproperty in HarmonyOS determines whether child components are clipped to the bounds of their parent. When set totrue, any overflow content is visually and interactively clipped. - The
.responseRegionproperty allows developers to define a custom touch response area for a component, which can be larger or smaller than its layout bounds. - The
Canvascomponent provides a drawing surface, andCanvasRenderingContext2Dis used to render shapes, text, and images on it.
Solution
The .clip property alone cannot achieve the desired behavior. When .clip is set to true, overflow content is not only visually clipped but also loses gesture responsiveness. Setting .clip to undefined disables clipping but does not control touch behavior.
To allow drawing beyond the parent component while restricting the touch-down response area, use the .responseRegion property. Here's how:
- Wrap the
Canvasin a parentColumnwith dimensions (150×150). - Set the
Canvassize to (300×300) to allow drawing beyond the parent. - Define a
.responseRegionof{ x: 75, y: 0, width: 150, height: 150 }so that only the central area responds to touch-down events. - Drawing can begin in the blue (responsive) area and extend into the red (non-responsive) area, but cannot start in the red zone.
Sample Code:
import { hilog } from '@kit.PerformanceAnalysisKit'
@Entry
@Component
struct DrawBankCom {
@State paintSize: number = 5
@State paintColor: Color = Color.Black
private settings: RenderingContextSettings = new RenderingContextSettings(true)
canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
tempPath: Path2D = new Path2D()
@State @Watch('onChange') pathArray: Array<Path2D> = []
@State isUpdate: boolean = true
@State removeArray: Array<Path2D> = []
@State text: string = ''
@State eventType: string = ''
@State arr: Array<number> = []
build() {
Column() {
if (this.isUpdate) {
Row() {
Column() {
Canvas(this.canvasContext)
.width(300)
.height(300)
.onReady(() => {
this.canvasContext.lineWidth = this.paintSize
this.canvasContext.stroke(this.tempPath)
for (let index = 0; index < this.pathArray.length; index++) {
this.canvasContext.stroke(this.pathArray[index])
}
})
.onTouch((event?: TouchEvent) => {
if (event) {
if (event.type === TouchType.Down) {
this.eventType = 'Down'
this.canvasContext.lineWidth = this.paintSize
this.canvasContext.beginPath()
this.tempPath = new Path2D()
this.pathArray.push(this.tempPath)
this.tempPath.moveTo(event.touches[0].x, event.touches[0].y)
this.canvasContext.moveTo(event.touches[0].x, event.touches[0].y)
}
if (event.type === TouchType.Up) {
this.eventType = 'Up'
}
if (event.type === TouchType.Move) {
this.eventType = 'Move'
this.tempPath.lineTo(event.touches[0].x, event.touches[0].y)
this.canvasContext.stroke(this.tempPath)
this.canvasContext.lineTo(event.touches[0].x, event.touches[0].y)
this.canvasContext.stroke()
}
this.text = 'TouchType:' + this.eventType + '\n touch point and touch element:\nx: '
+ event.touches[0].x + '\n' + 'y: ' + event.touches[0].y + '\nwidth:'
+ event.target.area.width + '\nheight:' + event.target.area.height + '\pathArray size:' +
this.pathArray.length
}
})
.renderFit(RenderFit.TOP_RIGHT)
.responseRegion({
x: 75,
y: 0,
width: 150,
height: 150
})
}
.width(150)
.height(150)
.backgroundColor('#ffbdf3e9')
}
}
Column() {
Row() {
Button('Undo').onClick(() => {
this.pathArray.pop()
hilog.info(0x0000, 'ccTest', this.pathArray.length + '')
})
}
Text(this.text)
}
}
.width(300)
.height(300)
.clip(false)
.borderRadius(10)
.backgroundColor('#ff0000')
}
onChange(changedPropertyName?: string) {
this.canvasContext.reset()
this.canvasContext.lineWidth = this.paintSize
for (let index = 0; index < this.pathArray.length; index++) {
this.canvasContext.stroke(this.pathArray[index])
}
}
}
Key Takeaways
- The
.clipproperty cannot selectively allow drawing overflow while restricting gesture response. - Use
.responseRegionto define a custom touch-sensitive area for theCanvas. - Drawing can extend beyond the parent component, but touch-down events will only register within the defined region.
- This approach enables flexible drawing behavior while maintaining controlled interaction zones.
Top comments (0)