Brush
ในองค์ประกอบจะอธิบายวิธีวาดสิ่งต่างๆ บนหน้าจอ โดยจะเป็นตัวกำหนดสีที่วาดในพื้นที่วาด (เช่น วงกลม สี่เหลี่ยมจัตุรัส เส้นทาง) แปรงในตัวที่มีประโยชน์สำหรับการวาดมีอยู่ 2-3 แบบ เช่น LinearGradient
, RadialGradient
หรือแปรงSolidColor
ธรรมดา
คุณสามารถใช้แปรงกับคอลวาด Modifier.background()
, TextStyle
หรือ DrawScope
เพื่อใช้รูปแบบการวาดกับเนื้อหาที่กำลังวาด
เช่น คุณสามารถใช้แปรงไล่ระดับสีแนวนอนเพื่อวาดวงกลมในDrawScope
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
แปรงไล่ระดับสี
มีแปรงไล่สีในตัวมากมายที่สามารถใช้เพื่อสร้างเอฟเฟกต์ไล่สีแบบต่างๆ แปรงเหล่านี้ช่วยให้คุณระบุรายการสีที่ต้องการสร้างการไล่ระดับได้
รายการแปรงไล่ระดับสีที่ใช้ได้และเอาต์พุตที่เกี่ยวข้อง
ประเภทแปรงไล่ระดับสี | เอาต์พุต |
---|---|
Brush.horizontalGradient(colorList) |
|
Brush.linearGradient(colorList) |
|
Brush.verticalGradient(colorList) |
|
Brush.sweepGradient(colorList)
หมายเหตุ: หากต้องการให้สีเปลี่ยนอย่างราบรื่น ให้ตั้งค่าสีสุดท้ายเป็นสีเริ่มต้น |
|
Brush.radialGradient(colorList) |
เปลี่ยนการกระจายสีด้วย colorStops
หากต้องการปรับแต่งลักษณะที่สีปรากฏในไล่ระดับสี ให้ปรับค่า colorStops
ของแต่ละสี colorStops
ควรระบุเป็นเศษส่วนระหว่าง 0 ถึง 1 ค่าที่มากกว่า 1 จะทำให้สีเหล่านั้นไม่แสดงผลเป็นส่วนหนึ่งของไล่ระดับ
คุณสามารถกําหนดค่าจุดหยุดสีให้มีจํานวนต่างกัน เช่น สีใดสีหนึ่งมีจำนวนน้อยหรือมาก
val colorStops = arrayOf( 0.0f to Color.Yellow, 0.2f to Color.Red, 1f to Color.Blue ) Box( modifier = Modifier .requiredSize(200.dp) .background(Brush.horizontalGradient(colorStops = colorStops)) )
สีจะกระจายอยู่ที่ออฟเซตที่ระบุตามที่ระบุไว้ในcolorStop
คู่ โดยสีเหลืองจะน้อยกว่าสีแดงและน้ำเงิน
วางรูปแบบซ้ำด้วย TileMode
แปรงไล่สีแต่ละรายการจะมีตัวเลือกในการตั้งค่า TileMode
คุณอาจไม่สังเกตเห็น TileMode
หากไม่ได้ตั้งค่าจุดเริ่มต้นและจุดสิ้นสุดของไล่ระดับสี เนื่องจากค่าเริ่มต้นจะเป็นแบบเต็มพื้นที่ TileMode
จะวางรูปแบบไล่ระดับสีเป็นตารางก็ต่อเมื่อพื้นที่มีขนาดใหญ่กว่าขนาดของแปรง
รหัสต่อไปนี้จะทำซ้ำรูปแบบไล่ระดับสี 4 ครั้ง เนื่องจากมีการตั้งค่า endX
เป็น 50.dp
และตั้งค่าขนาดเป็น 200.dp
val listColors = listOf(Color.Yellow, Color.Red, Color.Blue) val tileSize = with(LocalDensity.current) { 50.dp.toPx() } Box( modifier = Modifier .requiredSize(200.dp) .background( Brush.horizontalGradient( listColors, endX = tileSize, tileMode = TileMode.Repeated ) ) )
ตารางต่อไปนี้แสดงรายละเอียดว่าโหมดการ์ดแต่ละโหมดทําอะไรกับตัวอย่างHorizontalGradient
ด้านบน
TileMode | เอาต์พุต |
---|---|
TileMode.Repeated : ขอบจะซ้ำกันจากสีสุดท้ายไปจนถึงสีแรก |
|
TileMode.Mirror : ขอบจะสะท้อนจากสีสุดท้ายไปยังสีแรก |
|
TileMode.Clamp : ขอบถูกจำกัดให้เป็นสีสุดท้าย จากนั้นจะทาสีส่วนที่เหลือของภูมิภาคด้วยสีที่ใกล้เคียงที่สุด |
|
TileMode.Decal : แสดงผลไม่เกินขนาดของขอบเขต TileMode.Decal ใช้ประโยชน์จากสีดําโปร่งใสเพื่อสุ่มตัวอย่างเนื้อหาที่อยู่นอกขอบเขตเดิม ส่วน TileMode.Clamp จะสุ่มตัวอย่างสีขอบ |
TileMode
ทํางานในลักษณะที่คล้ายกันกับไล่ระดับสีตามทิศทางอื่นๆ ความแตกต่างคือทิศทางที่เกิดซ้ำ
เปลี่ยนขนาดแปรง
หากทราบขนาดของพื้นที่ที่จะวาดด้วยแปรง คุณสามารถตั้งค่าไทล์ endX
ตามที่ได้เห็นในส่วน TileMode
ด้านบน หากคุณอยู่ใน DrawScope
คุณสามารถใช้พร็อพเพอร์ตี้ size
ของ DrawScope
เพื่อดูขนาดของพื้นที่
หากไม่ทราบขนาดของพื้นที่วาด (เช่น หากกำหนด Brush
เป็นข้อความ) คุณสามารถขยาย Shader
และใช้ขนาดของพื้นที่วาดในฟังก์ชัน createShader
ได้
ในตัวอย่างนี้ ให้หารขนาดด้วย 4 เพื่อแสดงรูปแบบซ้ำ 4 ครั้ง
val listColors = listOf(Color.Yellow, Color.Red, Color.Blue) val customBrush = remember { object : ShaderBrush() { override fun createShader(size: Size): Shader { return LinearGradientShader( colors = listColors, from = Offset.Zero, to = Offset(size.width / 4f, 0f), tileMode = TileMode.Mirror ) } } } Box( modifier = Modifier .requiredSize(200.dp) .background(customBrush) )
นอกจากนี้ คุณยังเปลี่ยนขนาดแปรงของการไล่ระดับสีอื่นๆ เช่น การไล่ระดับสีแบบรัศมี ได้ด้วย หากไม่ได้ระบุขนาดและจุดกึ่งกลาง การไล่ระดับสีจะกินพื้นที่เต็มขอบเขตของ DrawScope
และจุดกึ่งกลางของการไล่ระดับสีแบบรัศมีจะเป็นจุดกึ่งกลางของขอบเขต DrawScope
โดยค่าเริ่มต้น ซึ่งส่งผลให้ศูนย์กลางของการไล่ระดับสีแบบรัศมีปรากฏเป็นศูนย์กลางของมิติข้อมูลขนาดเล็ก (ความกว้างหรือความสูง)
Box( modifier = Modifier .fillMaxSize() .background( Brush.radialGradient( listOf(Color(0xFF2be4dc), Color(0xFF243484)) ) ) )
เมื่อเปลี่ยนการไล่ระดับสีแบบรัศมีเพื่อกำหนดขนาดรัศมีเป็นมิติข้อมูลสูงสุด คุณจะเห็นผลลัพธ์การไล่ระดับสีแบบรัศมีที่ดีขึ้น
val largeRadialGradient = object : ShaderBrush() { override fun createShader(size: Size): Shader { val biggerDimension = maxOf(size.height, size.width) return RadialGradientShader( colors = listOf(Color(0xFF2be4dc), Color(0xFF243484)), center = size.center, radius = biggerDimension / 2f, colorStops = listOf(0f, 0.95f) ) } } Box( modifier = Modifier .fillMaxSize() .background(largeRadialGradient) )
โปรดทราบว่าขนาดจริงที่ส่งไปยังการสร้างโปรแกรมเปลี่ยนสีจะกำหนดจากตำแหน่งที่เรียกใช้ โดยค่าเริ่มต้น Brush
จะจัดสรร Shader
ใหม่ภายในหากขนาดแตกต่างจากการสร้าง Brush
ครั้งล่าสุด หรือหากออบเจ็กต์สถานะที่ใช้ในการสร้างโปรแกรมเปลี่ยนสีมีการเปลี่ยนแปลง
โค้ดต่อไปนี้จะสร้างโปรแกรมเปลี่ยนสี 3 ครั้งในขนาดที่แตกต่างกัน เนื่องจากขนาดของพื้นที่วาดภาพมีการเปลี่ยนแปลง
val colorStops = arrayOf( 0.0f to Color.Yellow, 0.2f to Color.Red, 1f to Color.Blue ) val brush = Brush.horizontalGradient(colorStops = colorStops) Box( modifier = Modifier .requiredSize(200.dp) .drawBehind { drawRect(brush = brush) // will allocate a shader to occupy the 200 x 200 dp drawing area inset(10f) { /* Will allocate a shader to occupy the 180 x 180 dp drawing area as the inset scope reduces the drawing area by 10 pixels on the left, top, right, bottom sides */ drawRect(brush = brush) inset(5f) { /* will allocate a shader to occupy the 170 x 170 dp drawing area as the inset scope reduces the drawing area by 5 pixels on the left, top, right, bottom sides */ drawRect(brush = brush) } } } )
ใช้รูปภาพเป็นแปรง
หากต้องการใช้ ImageBitmap เป็น Brush
ให้โหลดรูปภาพเป็น ImageBitmap
แล้วสร้างแปรง ImageShader
ดังนี้
val imageBrush = ShaderBrush(ImageShader(ImageBitmap.imageResource(id = R.drawable.dog))) // Use ImageShader Brush with background Box( modifier = Modifier .requiredSize(200.dp) .background(imageBrush) ) // Use ImageShader Brush with TextStyle Text( text = "Hello Android!", style = TextStyle( brush = imageBrush, fontWeight = FontWeight.ExtraBold, fontSize = 36.sp ) ) // Use ImageShader Brush with DrawScope#drawCircle() Canvas(onDraw = { drawCircle(imageBrush) }, modifier = Modifier.size(200.dp))
แปรงใช้กับภาพวาดหลายประเภท ได้แก่ พื้นหลัง ข้อความ และผืนผ้าใบ ซึ่งจะแสดงผลลัพธ์ต่อไปนี้
โปรดสังเกตว่าตอนนี้ข้อความยังได้รับการแสดงผลโดยใช้ ImageBitmap
เพื่อวาดพิกเซลของข้อความด้วย
ตัวอย่างขั้นสูง: แปรงที่กำหนดเอง
แปรง AGSL RuntimeShader
AGSL มีความสามารถของ Shader GLSL บางส่วน คุณเขียน Shader เป็น AGSL และใช้กับแปรงใน Compose ได้
หากต้องการสร้างแปรงชิดเดอร์ ให้กำหนดชิดเดอร์เป็นสตริงชิดเดอร์ AGSL ก่อน โดยทำดังนี้
@Language("AGSL") val CUSTOM_SHADER = """ uniform float2 resolution; layout(color) uniform half4 color; layout(color) uniform half4 color2; half4 main(in float2 fragCoord) { float2 uv = fragCoord/resolution.xy; float mixValue = distance(uv, vec2(0, 1)); return mix(color, color2, mixValue); } """.trimIndent()
Shader ด้านบนใช้สีอินพุต 2 สี คำนวณระยะทางจากด้านซ้ายล่าง (vec2(0, 1)
) ของพื้นที่วาด และทำการ mix
ระหว่าง 2 สีโดยอิงตามระยะทาง ซึ่งจะทำให้เกิดเอฟเฟกต์การไล่ระดับสี
จากนั้นสร้างแปรงแรเงา และตั้งค่าแบบคงที่สำหรับ resolution
ซึ่งเป็นขนาดของพื้นที่วาด และ color
และ color2
ที่ต้องการใช้เป็นอินพุตสำหรับไล่ระดับสีที่กำหนดเอง
val Coral = Color(0xFFF3A397) val LightYellow = Color(0xFFF8EE94) @RequiresApi(Build.VERSION_CODES.TIRAMISU) @Composable @Preview fun ShaderBrushExample() { Box( modifier = Modifier .drawWithCache { val shader = RuntimeShader(CUSTOM_SHADER) val shaderBrush = ShaderBrush(shader) shader.setFloatUniform("resolution", size.width, size.height) onDrawBehind { shader.setColorUniform( "color", android.graphics.Color.valueOf( LightYellow.red, LightYellow.green, LightYellow .blue, LightYellow.alpha ) ) shader.setColorUniform( "color2", android.graphics.Color.valueOf( Coral.red, Coral.green, Coral.blue, Coral.alpha ) ) drawRect(shaderBrush) } } .fillMaxWidth() .height(200.dp) ) }
เมื่อเรียกใช้ คุณจะเห็นว่าระบบแสดงผลต่อไปนี้บนหน้าจอ
โปรดทราบว่าคุณใช้เฉดสีทำอะไรได้มากกว่าแค่การทำเกล็ดสี เนื่องจากเป็นการคำนวณทั้งหมดที่อิงตามคณิตศาสตร์ ดูข้อมูลเพิ่มเติมเกี่ยวกับ AGSL ได้ที่เอกสารประกอบ AGSL
แหล่งข้อมูลเพิ่มเติม
ดูตัวอย่างเพิ่มเติมในการใช้แปรงในเครื่องมือเขียนได้ที่แหล่งข้อมูลต่อไปนี้
- การทำให้แปรงเคลื่อนไหวเพื่อระบายสีข้อความในเครื่องมือเขียน 🖌️
- กราฟิกและเลย์เอาต์ที่กำหนดเองใน Compose - Android Dev Summit 2022
- JetLagged Sample - RuntimeShader Brush
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- ตัวแก้ไขกราฟิก
- กราฟิกในเครื่องมือเขียน
- จัดรูปแบบข้อความ