Android Jetpack Compose, Canvas (1)
Search
๐ŸฅŠ

Android Jetpack Compose, Canvas (1)

์ƒ์„ฑ์ผ
2022/12/08 07:17
ํƒœ๊ทธ

Skia

Android Canvas๋ฅผ ์„ค๋ช…ํ•˜๊ธฐ์— ์•ž์„œ ๋จผ์ € Skia์— ๋Œ€ํ•ด ์•Œ์•„์•ผ ํ•œ๋‹ค. Skia๋Š” ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ(ํ•˜๋“œ์›จ์–ด ๋ฐ ์†Œํ”„ํŠธ์›จ์–ด)์—์„œ ์ž‘๋™ํ•˜๋Š” 2D ๊ทธ๋ž˜ํ”ฝ ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฉฐ, ์ฃผ๋กœ ๊ตฌ๊ธ€์—์„œ ๊ด€๋ฆฌํ•œ๋‹ค. ์ , ์„ , ๋ฉด์€ ๋ฌผ๋ก  ๊ทธ๋ฆด ์ˆ˜ ์žˆ๊ณ  ์ด๋ฏธ์ง€๋‚˜ ํ…์ŠคํŠธ ๋“ฑ๋„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ๋‹ค.

์ง€์› ํ”Œ๋žซํผ

โ€ข
Windows, macOS
โ€ข
iOS, Android, Flutter
โ€ข
Ubuntu, Debian, openSUSE, Fedora
โ€ข
Chrome, Firefox
โ€ข
๋“ฑ๋“ฑ

Android Canvas

์ขŒํ‘œ๊ณ„

Canvas(Skia)์˜ ์ขŒํ‘œ๊ณ„๋Š” ์ขŒ์ƒ๋‹จ ๊ผญ์ง€์ ์„ (0,0)์œผ๋กœ x๋Š” ์˜ค๋ฅธ์ชฝ ๋ฐฉํ–ฅ, y๋Š” ์•„๋ž˜ ๋ฐฉํ–ฅ์œผ๋กœ ์ฆ๊ฐ€ํ•˜๋Š” ์ขŒํ‘œ๊ณ„์ด๋‹ค.

๋ฐฉ๋ฒ•

๊ธฐ์กด์— xml ๋ฐฉ์‹์œผ๋กœ ๋ทฐ๋ฅผ ๊ทธ๋ ค ๋‚˜๊ฐˆ ๋•Œ๋Š” Canvas๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ๊ต‰์žฅํžˆ ๋ถˆํŽธํ–ˆ๋˜๊ฑฐ์— ๋น„ํ•ด Compose๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ Canvas๊ฐ€ ์•„์ฃผ ๊ฐ„ํŽธํ•ด์กŒ๋‹ค. Canvas๋ฅผ ๊ทธ๋ฆฌ๋ ค๋ฉด ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.
1.
Canvas()
@Composable fun MyCanvas() { Canvas( modifier = Modifier .size(300.dp) ) { // TODO } }
Kotlin
2.
Modifier.draw
โ€ข
Modifier.drawBehind: ๊ธฐ์กด ์ปดํฌ๋„ŒํŠธ์˜ ๋’ค, ์ปดํฌ๋„ŒํŠธ์˜ ๋ฐฐ๊ฒฝ์— ๊ทธ๋ฆฐ๋‹ค
โ€ข
Modifier.drawWithContent: ๊ธฐ์กด ์ปดํฌ๋„ŒํŠธ์˜ ์•ž, ์ปดํฌ๋„ŒํŠธ ์œ„์— ๊ทธ๋ฆฐ๋‹ค
@Composable fun MyCanvas2() { Column( modifier = Modifier.drawBehind{ // TODO } ) }
Kotlin

ํ•ต์‹ฌ

DrawScope ์•ˆ์—์„œ Canvas๋ฅผ ๊ทธ๋ ค ๋‚˜๊ฐˆํ…๋ฐ, ์—ฌ๊ธฐ์„œ ๋‘ ๊ฐ€์ง€ Parameter๋งŒ ์ž˜ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. center์™€ size์ด๋‹ค.
โ€ข
size: Canvas์˜ size์ด๋‹ค.
โ€ข
center: Canvas์˜ x,y ์ค‘์•™ Offset๊ฐ’์ด๋‹ค.
์ด ๋‘ ๊ฐ€์ง€๋ฅผ ์ด์šฉํ•ด ์›ํ•˜๋Š” ๋ชจ๋“  ๊ฒƒ์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ๋‹ค. ์ž์„ธํ•œ ํ™œ์šฉ ๋ฐฉ๋ฒ•์€ ๋ฐ‘์— ์˜ˆ์ œ์—์„œ ๊ณ„์†๋œ๋‹ค.

ํŒ

3.dp.toPx()
Kotlin
์‚ฌ์ด์ฆˆ์˜ ๊ฒฝ์šฐ 100dp์™€ ๊ฐ™์ด dp ๊ฐ’์„ ์ค„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, 100f ์ฒ˜๋Ÿผ ํ”ฝ์…€(Float) ๊ฐ’์œผ๋กœ๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค. dp๋ฅผ ํ”ฝ์…€๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์œ„์™€ ๊ฐ™์ด dp.toPx()๋ฅผ ์‚ฌ์šฉํ•˜๊ธธ ๋ฐ”๋ž€๋‹ค.

์˜ˆ์ œ

์œ„์˜ ๊ท€์—ฝ๊ณ  ๊นœ์ฐํ•œ ์–ผ๊ตด์„ ๊ทธ๋ ค๋ณด์ž.

Canvas()

@Composable fun MyFace() { Canvas(modifier = Modifier.size(300.dp)) }
Kotlin
์•ž์„œ ์„ค๋ช… ํ–ˆ๋“ฏ์ด Jetpack Compose์—์„œ Canvas๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋Š” ๊ต‰์žฅํžˆ ์‰ฝ๋‹ค.
์—ฌ๊ธฐ์„œ ์œ ์˜ํ•ด์•ผํ•  ์ ์€ ํฌ๊ธฐ๋ฅผ ๋ฐ˜๋“œ์‹œ ๋ช…์‹œํ•ด์ค˜์•ผ ํ•œ๋‹ค. Modifier.size()๋ฅผ ํ†ตํ•ด ๋ช…์‹œํ•ด์ฃผ๊ฑฐ๋‚˜, Modifier.fillMaxSize()๋ฅผ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. (Modifier.fillMaxWidth()๋‚˜, Modifier.fillMaxHeight() ์ค‘์— ํ•˜๋‚˜๋งŒ ์จ์„œ๋„ ์•ˆ๋œ๋‹ค. ๊ฐ€๋กœ์™€ ์„ธ๋กœ์— ๋Œ€ํ•ด ํ™•์‹คํ•˜๊ฒŒ ๋ช…์‹œ ํ•„์š”)

์ค€๋น„๋ฌผ

์–ผ๊ตด์„ ๊ทธ๋ฆฌ๊ธฐ ์•ž์„œ, ์ค€๋น„๋ฌผ์„ ์ฑ™๊ฒจ๋ณด์ž
โ€ข
drawCircle(): ๋™๊ทธ๋ผ๋ฏธ
โ€ข
drawOval(): ํƒ€์›
โ€ข
drawRect(): ์‚ฌ๊ฐํ˜•
โ€ข
drawArc(): ์•„์น˜, ํ˜ธ, ์›ํ˜ธ ๋“ฑ๋“ฑ
ํ•˜๋‚˜ ๋”, ์œ„ ์ค€๋น„๋ฌผ์— topLeft ํ˜น์€ center ๊ฐ’์„ ์ง€์ •ํ•ด ์ค˜์•ผ ํ•˜๋Š”๋ฐ ๋‘˜ ๋‹ค ๊ฒฐ๊ตญ Offset ๊ฐ’์„ ์ฃผ๋ฉด ๋œ๋‹ค.
size์™€ radius๋„ ๊ฒฐ๊ตญ ํฌ๊ธฐ(Float, Size) ๊ฐ’์ด๋‹ค.

Canvas ์‚ฌ์ด์ฆˆ ๋ฐ ๋ฐฐ๊ฒฝ์ƒ‰ ์ง€์ •

@Composable fun MyFace() { Canvas( modifier = Modifier .background(color = Color.LightGray) .fillMaxSize() ) }
Kotlin
ํšŒ์ƒ‰์œผ๋กœ ์ง€์ •ํ•ด์ค€ ํ›„ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์น™์น™ํ•œ ํ™”๋ฉด์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

ํ”ผ๋ถ€์ƒ‰(?), ๋ฐฐ๊ฒฝ ์ž…ํžˆ๊ธฐ

@Composable fun MyFace() { Canvas( modifier = Modifier .background(color = Color.Yellow) .size(300.dp) ) { drawRect(color = Color.Yellow, topLeft = Offset(0f, 0f), size = size) } }
Kotlin
์‚ด์ง ์–ต์ง€์Šค๋Ÿฝ์ง€๋งŒ size๋ฅผ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•ด Canvas ์œ„์— drawRect()๋ฅผ ์ด์šฉํ•ด Canvas ํฌ๊ธฐ์™€ ๋™์ผํ•œ ํฌ๊ธฐ์˜ ๋…ธ๋ž€ ๋ฐฐ๊ฒฝ์„ ์˜ฌ๋ ค๋ณธ๋‹ค.
Canvas์˜ ์ขŒํ‘œ๊ณ„์— ๋”ฐ๋ผ ์ขŒ์ƒ๋‹จ(topLeft = Offset(0f,0f))์—์„œ ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ณ , size๋Š” (drawScope.)size๋ฅผ ์ž…๋ ฅํ•ด์ฃผ๋ฉด Canvas์˜ ํฌ๊ธฐ์™€ ๊ฐ™์€ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ ์šฉ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

์–ผ๊ตด ์œค๊ณฝ ๋“œ๋กœ์ž‰

@Composable fun MyFace() { Canvas( modifier = Modifier .background(color = Color.Yellow) .size(300.dp) ) { drawCircle( color = Color.Black, center = center, style = Stroke( width = 3.dp.toPx(), ), radius = 210f ) // TODO } }
Kotlin
drawCircle()์„ ์‚ฌ์šฉํ•ด ์–ผ๊ตด ์œค๊ณฝ์„ ์žก์•„๋ณด์ž. (์•ž์œผ๋กœ ๊ณ„์† ์ถ”๊ฐ€๋˜๋Š” ์ฝ”๋“œ๋Š” ์œ„์— ๋ณด์ด๋Š” ์ฝ”๋“œ์˜ โ€œ// TODOโ€ ๋ถ€ํ„ฐ ์•„๋ž˜๋กœ ์ด์–ด ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค)
center ๊ฐ’์— Offset(0f, 0f)๋ฅผ ์ฃผ๊ฒŒ ๋˜๋ฉด ์ขŒ์ธก ์ƒ๋‹จ์— ๋™๊ทธ๋ผ๋ฏธ๋ฅผ ๊ทธ๋ฆฌ๋Š”๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
center ๊ฐ’์— center๋ฅผ ์ฃผ๊ฒŒ ๋˜๋ฉด Canvas์˜ ์ • ์ค‘์•™์— ์œ„์น˜ํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
style ๊ฐ’์„ ๋”ฐ๋กœ ๋ช…์‹œํ•˜์ง€ ์•Š์œผ๋ฉด Fill์ด ๊ธฐ๋ณธ๊ฐ’์ด๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์€ ๋™๊ทธ๋ผ๋ฏธ๊ฐ€ ๊ทธ๋ ค์ง„๋‹ค.
์•ž์„œ ์„ค๋ช…ํ•œ center ๊ฐ’์— center(Offset)์„ ์ง€์ •ํ•ด์ฃผ๋ฉด ์ •์ค‘์•™์— ์œ„์น˜ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
style์˜ ๊ฒฝ์šฐ ๊ธฐ๋ณธ๊ฐ’์ด Fill๋กœ ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋”ฐ๋กœ ๋ช…์‹œํ•˜์ง€ ์•Š์œผ๋ฉด ๊ฒ€์€์ƒ‰์œผ๋กœ ๊ฐ€๋“ ์ฑ„์›Œ์ง„ ๋™๊ทธ๋ผ๋ฏธ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฒˆ ์˜ˆ์ œ์—์„œ๋Š” Stroke๋กœ ์–ผ๊ตด ์œค๊ณฝ๋งŒ ์žก์•„์ค€๋‹ค.

์ฝ” ๋“œ๋กœ์ž‰

์ด์ œ ์ฝ”๋ฅผ ๊ทธ๋ ค์ค„ํ…๋ฐ, ์ฝ”๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ˆˆ, ๋ˆˆ์น, ์ž…, ๋จธ๋ฆฌ์นด๋ฝ์˜ ์œ„์น˜๋ฅผ ์ •ํ•ด์ค„ ๊ฒƒ์ด๋‹ค.
@Composable fun MyFace() { Canvas( modifier = Modifier .background(color = Color.LightGray) .fillMaxSize() ) { val noseOffset = Offset(center.x - 25f, center.y - 90f) val leftEyeOffset = Offset(center.x / 2 + (center.x / 4), center.y - 60f) val rightEyeOffset = Offset(center.x + (center.x / 4), center.y - 60f) ...
Kotlin
noseOffset์ด๋ผ๋Š” ๊ฐ’์„ ๋งŒ๋“ค์–ด ์ฝ”์˜ ์œ„์น˜๋ฅผ center.x ๋ณด๋‹ค 25 ์™ผ์ชฝ์—, center.y ๋ณด๋‹ค 90 ์œ„์— ๊ทธ๋ ค์ค„ ๊ฒƒ์ด๋‹ค.
์—ฌ๊ธฐ์„œ x๊ฐ’์„ -25 ํ•ด ์ฃผ๋Š” ์ด์œ ๋Š” ์ฝ”์˜ ํฌ๊ธฐ(width)๊ฐ€ 50์ด๊ธฐ ๋•Œ๋ฌธ์— ํฌ๊ธฐ(width)์˜ ์ ˆ๋ฐ˜ ๋งŒํผ ์™ผ์ชฝ์œผ๋กœ ์ด๋™์‹œ์ผœ ๋”ฑ ์ •์ค‘์•™์— ์œ„์น˜ํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. (์ด๋ฒˆ ์˜ˆ์ œ์—์„œ ์ฝ”์˜ ํฌ๊ธฐ ๋“ฑ์€ ๋ณ€์ˆ˜๋‚˜ ์ƒ์ˆ˜๋กœ ์ง€์ •ํ•˜์ง€ ์•Š๊ณ  ์ง„ํ–‰ํ•œ๋‹ค.)
noseOffset์—์„œ x๊ฐ’์„ center.x๋กœ๋งŒ ์ฃผ์—ˆ์„ ๋•Œ ๊ทธ๋ ค์ง€๋Š” ์ฝ”์˜ ์œ„์น˜
noseOffset์—์„œ x๊ฐ’์„ center.x - 25๋กœ ์ฃผ์—ˆ์„ ๋•Œ ๊ทธ๋ ค์ง€๋Š” ์ฝ”์˜ ์œ„์น˜
์™ผ์ชฝ๋ˆˆ(leftEyeOffset)๊ณผ ์˜ค๋ฅธ์ชฝ๋ˆˆ(rightEyeOffset)๋„ center๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ๊ณ„์‚ฐํ–ˆ๋‹ค.

๋ˆˆ ๋“œ๋กœ์ž‰

drawCircle( brush = Brush.radialGradient( colors = listOf( Color.White, Color.Black ), center = leftEyeOffset, radius = 25f ), radius = 25f, center = leftEyeOffset ) drawCircle( brush = Brush.radialGradient( colors = listOf( Color.White, Color.Black ), center = rightEyeOffset, radius = 25f ), radius = 25f, center = rightEyeOffset )
Kotlin
drawCircle() ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ๋ณด๋ฉด Brush๋ฅผ ์‚ฌ์šฉํ•ด๋„, Color๋ฅผ ์‚ฌ์šฉํ•ด๋„ ์ข‹๋‹ค. Color์™€ Style๋ฅผ ๊ฐ€์ง€๊ณ  ๋ˆˆ์„ ๊ทธ๋ ค๋„ ์ข‹์ง€๋งŒ, ์ด๋ฒˆ ์˜ˆ์ œ์—์„œ๋Š” ๊ทธ๋ผ๋ฐ์ด์…˜์„ ๋„ฃ๊ธฐ ์œ„ํ•ด Brush๋กœ ๋ˆˆ์„ ๊ทธ๋ฆฐ๋‹ค.
radius๋Š” ๋‘ ๊ฐœ๊ฐ€ ๋ณด์ด๋Š”๋ฐ, ์œ„์—์„œ ๋ถ€ํ„ฐ ๊ฐ๊ฐ ๊ทธ๋ผ๋ฐ์ด์…˜์˜ ํฌ๊ธฐ, ๋™๊ทธ๋ผ๋ฏธ์˜ ํฌ๊ธฐ๋ฅผ ์ง€์ •ํ•œ๋‹ค. ์ฆ‰ ๋ˆˆ์˜ ํฌ๊ธฐ๋Š” 25f์ด๋‹ค.
์ง€๊ธˆ๊นŒ์ง€์˜ ๊ฒฐ๊ณผ๋ฌผ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

๋ˆˆ์น ๋“œ๋กœ์ž‰

drawRect( color = Color.Black, topLeft = Offset((rightEyeOffset.x - 37.5f), rightEyeOffset.y - 50f), size = Size(80f, 10f) ) drawRect( color = Color.Black, topLeft = Offset((leftEyeOffset.x - 37.5f), leftEyeOffset.y - 50f), size = Size(80f, 10f) )
Kotlin
๊ฒฐ๊ตญ ์ค‘์š”ํ•œ ๊ฒƒ์€ topLeft์— ์ง€์ •ํ•  Offset์ด๋‹ค. center๊ฐ’๊ณผ ๊ทธ๋ฆฌ๋ ค๋Š” ์‚ฌ๊ฐํ˜•์˜ ํฌ๊ธฐ๋ฅผ ๊ณ„์‚ฐํ•ด์„œ ์ ๋‹นํ•œ ์œ„์น˜๋กœ ์ง€์ •ํ•ด ์ฃผ์ž. ์‚ฌ๊ฐํ˜•์˜ ํฌ๊ธฐ๋„ ์ ๋‹นํ•œ ๊ฐ’์œผ๋กœ ์ง€์ •ํ•ด ์†ก์Šนํ—Œ ๋บจ์น˜๋Š” ์†ก์ถฉ์ด ๋ˆˆ์น์„ ๊ทธ๋ ค์ฃผ์ž. ๊ฒฐ๊ณผ๋ฌผ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

์ž… ๋“œ๋กœ์ž‰

drawArc( color = Color.Black, startAngle = 0f, sweepAngle = 180f, useCenter = true, topLeft = Offset(center.x - 100f, noseOffset.y + 60f), size = Size(200f, 200f), style = Stroke( width = 3.dp.toPx() ) )
Kotlin
์ž… ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” drawArc()๋ฅผ ์ด์šฉํ•ด ๊ทธ๋ ค์ค„ํ…๋ฐ, ๋ญ”๊ฐ€ ๋ณต์žกํ•˜๋‹ค. topLeft, size, style์€ ๋ญ”์ง€ ์•ž์„œ ๋‹ค๋ค˜๋‹ค.
๊ทธ๋ ‡๋‹ค๋ฉด startAngle, sweepAngle, useCenter๋Š” ๋ฌด์—‡์ธ๊ฐ€? startAngle๊ณผ sweepAngle ๋ถ€ํ„ฐ ์งš๊ณ  ๋„˜์–ด๊ฐ€ ๋ณด์ž.
drawArc( color = Color.Black, startAngle = 0f, sweepAngle = 90f, useCenter = true, topLeft = Offset(center.x - 100f, noseOffset.y + 60f), size = Size(200f, 200f), /*style = Stroke( width = 3.dp.toPx() )*/ )
Kotlin
style์„ ๊ธฐ๋ณธ ๊ฐ’์ธ Fill๋กœ ์ฃผ๋ฉด ์กฐ๊ธˆ ๋” ์ดํ•ด๊ฐ€ ์‰ฝ๋‹ค. startAngle๋Š” ์‹œ์ž‘์ ์ด 0f๋ผ๋Š” ๊ฒƒ์ด์ง€๋งŒ, ์—ฌ๊ธฐ์„œ 0f๋Š” 3์‹œ ๋ฐฉํ–ฅ์ด๋‹ค. 3์‹œ ๋ฐฉํ–ฅ๋ถ€ํ„ฐ 90๋„์˜ ๊ฐ’์„ ์ค€ ๊ฒฐ๊ณผ ํ™”๋ฉด์ด ๋ฐ”๋กœ ์œ„์™€ ๊ฐ™๋‹ค.
3์‹œ ๋ฐฉํ–ฅ ๋ถ€ํ„ฐ, 180๋„
3์‹œ ๋ฐฉํ–ฅ ๋ถ€ํ„ฐ, 280๋„
3์‹œ ๋ฐฉํ–ฅ ๋ถ€ํ„ฐ, 360๋„
์—ฌ๊ธฐ์„œ ์Šคํƒ€์ผ์„ Stroke๋กœ ์ฃผ๊ฒŒ ๋˜๋ฉด?
Stroke๋กœ 3์‹œ ๋ฐฉํ–ฅ ๋ถ€ํ„ฐ, 180๋„
Stroke๋กœ 3์‹œ ๋ฐฉํ–ฅ ๋ถ€ํ„ฐ, 280๋„
Stroke๋กœ 3์‹œ ๋ฐฉํ–ฅ ๋ถ€ํ„ฐ, 360๋„
์—ฌ๊ธฐ์„œ useCenter ๊ฐ’์„ true โ†’ false๋กœ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋˜๋ฉด?
90๋„
180๋„
280๋„
360๋„
useCenter๊ฐ’์„ true๋กœ ์ฃผ๋ฉด ๋™๊ทธ๋ผ๋ฏธ์˜ ์ค‘์‹ฌ์ ์„ ์—ฐ๊ฒฐํ•ด ๊ทธ๋ ค์ฃผ์ง€๋งŒ, false๋กœ ์ฃผ๋ฉด ์ค‘์‹ฌ์ ๊ณผ ์—ฐ๊ฒฐํ•ด ์ฃผ์ง€ ์•Š๋Š”๋‹ค.
drawArc( color = Color.Black, startAngle = 0f, sweepAngle = 360f, useCenter = true, topLeft = Offset(center.x - 100f, noseOffset.y + 60f), size = Size(200f, 200f), style = Stroke( width = 3.dp.toPx() ) )
Kotlin
์˜ˆ์ œ ๋Œ€๋กœ ์ž…์„ ๋“œ๋กœ์ž‰ํ•ด๋ณด์ž๋ฉด, ๊ฒฐ๊ณผ๋ฌผ์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

๋ˆˆ์น ๋“œ๋กœ์ž‰

drawLine( color = Color.Black, start = Offset(center.x, center.y - 210f), end = Offset(center.x, center.y - 210f - 80f), strokeWidth = 4.dp.toPx() ) drawLine( color = Color.Black, start = Offset(center.x - 30f, center.y - 210f), end = Offset(center.x - 30f, center.y - 210f - 80f), strokeWidth = 4.dp.toPx() ) drawLine( color = Color.Black, start = Offset(center.x + 30f, center.y - 210f), end = Offset(center.x + 30f, center.y - 210f - 80f), strokeWidth = 4.dp.toPx() )
Kotlin
๋ˆˆ์น์„ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•œ drawLine()์€ start์™€ end๋ผ๋Š” Offset ๊ฐ’๋งŒ ์ž˜ ์ง€์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ์ด๊ฒƒ ์—ญ์‹œ center ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ์œ„์น˜๋ฅผ ์žก๋Š”๋‹ค.

์ตœ์ข… ์ฝ”๋“œ ๋ฐ ๊ฒฐ๊ณผ๋ฌผ

@Composable fun MyFace() { Canvas( modifier = Modifier .background(color = Color.LightGray) .fillMaxSize() ) { val noseOffset = Offset(center.x - 25f, center.y - 90f) val leftEyeOffset = Offset(center.x / 2 + (center.x / 4), center.y - 60f) val rightEyeOffset = Offset(center.x + (center.x / 4), center.y - 60f) drawRect(color = Color.Yellow, topLeft = Offset(0f, 0f), size = size) drawCircle( color = Color.Black, center = center, style = Stroke( width = 3.dp.toPx(), ), radius = 210f ) drawOval( color = Color.Black, size = Size(50f, 120f), topLeft = noseOffset, style = Stroke( width = 3.dp.toPx(), ) ) drawCircle( brush = Brush.radialGradient( colors = listOf( Color.White, Color.Black ), center = leftEyeOffset, radius = 25f ), radius = 25f, center = leftEyeOffset ) drawCircle( brush = Brush.radialGradient( colors = listOf( Color.White, Color.Black ), center = rightEyeOffset, radius = 25f ), radius = 25f, center = rightEyeOffset ) drawRect( color = Color.Black, topLeft = Offset((rightEyeOffset.x - 37.5f), rightEyeOffset.y - 50f), size = Size(80f, 10f) ) drawRect( color = Color.Black, topLeft = Offset((leftEyeOffset.x - 37.5f), leftEyeOffset.y - 50f), size = Size(80f, 10f) ) drawArc( color = Color.Black, startAngle = 0f, sweepAngle = 180f, useCenter = true, topLeft = Offset(center.x - 100f, noseOffset.y + 60f), size = Size(200f, 200f), style = Stroke( width = 3.dp.toPx() ) ) drawLine( color = Color.Black, start = Offset(center.x, center.y - 210f), end = Offset(center.x, center.y - 210f - 80f), strokeWidth = 4.dp.toPx() ) drawLine( color = Color.Black, start = Offset(center.x - 30f, center.y - 210f), end = Offset(center.x - 30f, center.y - 210f - 80f), strokeWidth = 4.dp.toPx() ) drawLine( color = Color.Black, start = Offset(center.x + 30f, center.y - 210f), end = Offset(center.x + 30f, center.y - 210f - 80f), strokeWidth = 4.dp.toPx() ) } }
Kotlin
์Šค์ผ€์น˜
์ตœ์ข… ๊ฒฐ๊ณผ๋ฌผ