Android Compose 自定義圓形取色盤

?

val Dp.toPx: Floatget() {var scale = 3f
//        MyApplication.context.resources.displayMetrics.apply {
//            scale = density
//        }return value * scale}val colors = List(360) { i ->Color.hsv(360f - i, 1f, 1f) // 360°到1°的所有HSV顏色
}@Preview
@Composable
fun ColorPicker(color: Color = Color(255, 0, 0),radius:Float = 12f,stroke:Float = 4f,colorCallback:(Color)->Unit = {}) {var x by rememberSaveable{ mutableFloatStateOf(-1f) }var y by rememberSaveable{ mutableFloatStateOf(-1f) }val initialHsv  = remember(color) {val (r, g, b) = colorrgbToHsv((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt())}var hsv by rememberSaveable{ mutableStateOf(initialHsv) }var sizeWidth = rememberSaveable{ 300f }LaunchedEffect(color) {val (r, g, b) = colorhsv = rgbToHsv((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt())angleToCoordinates(hsv[0],(sizeWidth - (radius.dp.toPx)) * hsv[1],sizeWidth,(radius.dp.toPx)).let {x = it.firsty = it.second}}LaunchedEffect(x,y) {colorCallback(Color.hsv(hsv[0],hsv[1],hsv[2]))}Canvas(modifier = Modifier.fillMaxWidth().aspectRatio(1f).padding(8.dp).onSizeChanged { size ->sizeWidth = size.width.toFloat() / 2fangleToCoordinates(hsv[0],(sizeWidth - (radius.dp.toPx)) * hsv[1],sizeWidth,(radius.dp.toPx)).let {x = it.firsty = it.second}}.pointerInput(Unit) {awaitEachGesture {val event = awaitPointerEvent(PointerEventPass.Initial)val widthSize: Floatif (event.changes.firstOrNull()?.changedToDown() == true) {widthSize = size.width.toFloat()var newValue = Offset(x = event.changes.first().position.x,y = event.changes.first().position.y)var circle = pointToCircle(newValue.x,newValue.y,widthSize)if (widthSize / 4f < circle &&widthSize / 2f > circle) {val xy =findPoint(newValue.x,newValue.y,circle,widthSize,radius.dp.toPx())hsv[0] = angle(xy.first, xy.second, widthSize)hsv[1] = (circle / (widthSize / 2)).coerceIn(0.5f, 1f)x = xy.firsty = xy.second}val down = awaitFirstDown(requireUnconsumed = true)var drag: PointerInputChange?do {drag = awaitTouchSlopOrCancellation(down.id) { change, _ ->if (change.positionChange() != Offset.Zero) change.consume()}} while (drag != null && !drag.isConsumed)if (drag != null) {!drag(drag.id) {newValue = Offset(x = it.position.x,y = it.position.y)circle = pointToCircle(newValue.x,newValue.y,widthSize)val xy = findPoint(newValue.x,newValue.y,circle,widthSize,radius.dp.toPx())hsv[0] = angle(xy.first, xy.second, widthSize)hsv[1] = (circle / (widthSize / 2)).coerceIn(0.5f, 1f)x = xy.firsty = xy.secondif (it.positionChange() != Offset.Zero) it.consume()}}}}}) {//初始化if(x == -1f){x = size.width - radius.dp.toPx() - strokey = size.height/2}//hsv的漸變色drawCircle(brush = Brush.sweepGradient(colors = colors),style = Stroke(width = size.width / 4),radius = size.width / 8 * 3)//中心漸變色drawCircle(brush = Brush.radialGradient(colors = listOf(Color.White,Color.Transparent),radius = size.width / 2),style = Stroke(width = size.width / 4),radius = size.width / 8 * 3)//外黑色邊框drawCircle(color= Color.Black,style = Stroke(width = stroke/5f),radius = size.width / 2)//內黑色邊框drawCircle(color= Color.Black,style = Stroke(width = stroke/5f),radius = size.width / 4)//圓drawCircle(color = Color.hsv(hsv[0],hsv[1],hsv[2]),radius = radius.dp.toPx(),style = Fill,center = Offset(x,y))//圓黑色邊框drawCircle(color = Color.Gray,radius = radius.dp.toPx(),style = Stroke(width = stroke),center = Offset(x,y))}
}@Preview
@Composable
fun ColorPicker2(color: Color = Color(255, 0, 0), radius:Float = 12f, stroke:Float = 4f, colorCallback:(Color)->Unit = {}) {var x by rememberSaveable{ mutableFloatStateOf(-1f) }var y by rememberSaveable{ mutableFloatStateOf(-1f) }val initialHsv  = remember(color) {val (r, g, b) = colorrgbToHsv((r * 255).toInt(),(g * 255).toInt(),(b * 255).toInt())}var hsv by rememberSaveable{ mutableStateOf(initialHsv) }var sizeWidth = rememberSaveable{ 300f }LaunchedEffect(color) {val (r, g, b) = colorhsv = rgbToHsv((r * 255).toInt(),(g * 255).toInt(),(b * 255).toInt())angleToCoordinates(hsv[0],(sizeWidth - (radius.dp.toPx)) * hsv[1],sizeWidth,(radius.dp.toPx)).let {x = it.firsty = it.second}}LaunchedEffect(x,y) {colorCallback(Color.hsv(hsv[0],hsv[1],hsv[2]))}Canvas(modifier = Modifier.fillMaxWidth().aspectRatio(1f).padding(8.dp).onSizeChanged { size ->sizeWidth = size.width.toFloat() / 2fangleToCoordinates(hsv[0],(sizeWidth - (radius.dp.toPx)) * hsv[1],sizeWidth,(radius.dp.toPx)).let {x = it.firsty = it.second}}.pointerInput(Unit) {awaitEachGesture {val event = awaitPointerEvent(PointerEventPass.Initial)val widthSize: Floatif (event.changes.firstOrNull()?.changedToDown() == true) {widthSize = size.width.toFloat()var newValue = Offset(x = event.changes.first().position.x,y = event.changes.first().position.y)var circle = pointToCircle(newValue.x, newValue.y, widthSize)if (widthSize / 2f > circle) {val xy = findPoint2(newValue.x, newValue.y, circle, widthSize, radius.dp.toPx())hsv[0] = angle(xy.first, xy.second, widthSize)hsv[1] = (circle / (widthSize / 2)).coerceIn(0f, 1f)x = xy.firsty = xy.second}val down = awaitFirstDown(requireUnconsumed = true)var drag: PointerInputChange?do {drag = awaitTouchSlopOrCancellation(down.id) { change, _ ->if (change.positionChange() != Offset.Zero) change.consume()}} while (drag != null && !drag.isConsumed)if (drag != null) {!drag(drag.id) {newValue = Offset(x = it.position.x,y = it.position.y)circle = pointToCircle(newValue.x, newValue.y, widthSize)val xy = findPoint2(newValue.x,newValue.y,circle,widthSize,radius.dp.toPx())hsv[0] = angle(xy.first, xy.second, widthSize)hsv[1] = (circle / (widthSize / 2)).coerceIn(0f, 1f)x = xy.firsty = xy.secondif (it.positionChange() != Offset.Zero) it.consume()}}}}}) {//初始化if(x == -1f){x = size.width - radius.dp.toPx() - strokey = size.height/2}//hsv的漸變色drawCircle(brush = Brush.sweepGradient(colors = colors),style = Fill,radius = size.width / 2)//中心漸變色drawCircle(brush = Brush.radialGradient(colors = listOf(Color.White,Color.Transparent),radius = size.width / 2),style = Fill,radius = size.width / 2)//外黑色邊框drawCircle(color= Color.Black,style = Stroke(width = stroke/5f),radius = size.width / 2)//圓drawCircle(color = Color.hsv(hsv[0],hsv[1],hsv[2]),radius = radius.dp.toPx(),style = Fill,center = Offset(x,y))//圓黑色邊框drawCircle(color = Color.Gray,radius = radius.dp.toPx(),style = Stroke(width = stroke),center = Offset(x,y))}
}// 離目標最近的點 待開發
private fun findPoint(x1: Float,y1: Float,circle: Float,size:Float,colorRadius:Float,colorStroke:Float = 0f
): Pair<Float,Float>{var r = size / 2f - (colorRadius + colorStroke)when{circle > (size/2f - colorRadius)->{}circle < (size/4f + colorRadius)->{r = size / 4f + (colorRadius + colorStroke)}else -> {return x1 to y1}}val a = y1 - size / 2fval b = x1 - size / 2fvar yColor = sqrt((r * r) / ((b / a) * (b / a) + 1))if (a < 0) yColor = -yColorval xColor = (b * yColor) / a + size / 2fyColor += size / 2freturn xColor to yColor
}// 離目標最近的點 待開發
private fun findPoint2(x1: Float,y1: Float,circle: Float,size:Float,colorRadius:Float,colorStroke:Float = 0f
): Pair<Float,Float>{var r = size / 2f - (colorRadius + colorStroke)when{circle > (size/2f - colorRadius)->{}else -> {return x1 to y1}}val a = y1 - size / 2fval b = x1 - size / 2fvar yColor = sqrt((r * r) / ((b / a) * (b / a) + 1))if (a < 0) yColor = -yColorval xColor = (b * yColor) / a + size / 2fyColor += size / 2freturn xColor to yColor
}// 點到圓心距離
fun pointToCircle(x: Float, y: Float, size:Float) =sqrt((x - size / 2f).pow(2) + (y - size / 2f).pow(2))private const val RAD_TO_DEG = 180f / Math.PI.toFloat()// 角度
private fun angle(x: Float, y: Float, size: Float): Float {val dx = x - size / 2fval dy = y - size / 2fvar deg = atan2(-dy, dx) * RAD_TO_DEGif (deg < 0) deg += 360freturn deg.coerceIn(0f,360f)
}private const val DEG_TO_RAD = Math.PI.toFloat() / 180fprivate fun angleToCoordinates(angleDeg: Float, radius: Float, _radius: Float, size: Float): Pair<Float, Float> {// 將角度轉換為弧度val angleRad = angleDeg * DEG_TO_RAD// 計算x和y坐標(相對于中心)val dx = radius * cos(angleRad)val dy = -radius * sin(angleRad)// 轉換為絕對坐標val x = dx + _radiusval y = dy + _radiusreturn Pair(x, y)
}fun rgbToHsv(r: Int, g: Int, b: Int): FloatArray {// 參數校驗require(r in 0..255) { "Red component must be between 0 and 255" }require(g in 0..255) { "Green component must be between 0 and 255" }require(b in 0..255) { "Blue component must be between 0 and 255" }// 將RGB分量歸一化到[0,1]范圍val rNorm = r / 255fval gNorm = g / 255fval bNorm = b / 255fval cmax = max(max(rNorm, gNorm), bNorm)val cmin = min(min(rNorm, gNorm), bNorm)val delta = cmax - cmin// 計算色相(H)val h = when {delta == 0f -> 0fcmax == rNorm -> 60f * (((gNorm - bNorm) / delta) % 6f)cmax == gNorm -> 60f * (((bNorm - rNorm) / delta) + 2f)else -> 60f * (((rNorm - gNorm) / delta) + 4f)}.let { if (it < 0) it + 360f else it }// 計算飽和度(S)val s = when {cmax == 0f -> 0felse -> delta / cmax}// 明度(V)就是cmaxval v = cmaxreturn floatArrayOf(h, s, v)
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/83389.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/83389.shtml
英文地址,請注明出處:http://en.pswp.cn/web/83389.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vscode 配置 latex

下載插件 安裝插件前自行安裝 texlive, 按照 https://tug.org/texlive/ 要求安裝 找到 settings 打開 json 文件 在 json 文件中添加如下配置 "latex-workshop.latex.tools": [{"name": "latexmk","command": "latexmk",&qu…

安寶特方案丨船舶智造的“AR+AI+作業標準化管理解決方案”(質檢)

船舶質檢管理現狀&#xff1a;質檢環節部分依賴人工檢測&#xff0c;質檢員依據質量標準對產品進行抽檢或全檢。人工質檢受質檢員主觀因素影響較大&#xff0c;不同質檢員對標準的把握可能存在差異。 一、痛點與需求 1 Arbigtec 人工經驗依賴嚴重&#xff1a; 質檢員的檢測準確…

jenkins gerrit-trigger插件配置

插件gerrit-trigger下載好之后要在Manage Jenkins -->Gerrit Trigger-->New Server 中新增Gerrit Servers 配置好保存后點擊“狀態”查看是否正常

ubuntu24.04下 zookeeper3.8.4 集群的配置

1、環境 1.1 三臺機器網絡互通&#xff0c;并做hosts解析 準備三臺及以上ubuntu24.04主機&#xff08;奇數&#xff09; rootzk-node01:~# hostname zk-node01rootzk-node01:~# cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 u24-server10.0.49.215 zk-node01 10.0.4…

火山引擎 veFuser:面向擴散模型的圖像與視頻生成推理服務框架

資料來源&#xff1a;火山引擎-開發者社區 DiT 模型與推理挑戰 近年來&#xff0c;擴散模型&#xff08;Diffusion Models&#xff09;在生成式人工智能領域取得了突破性進展&#xff0c;尤其是在圖像和視頻生成方面表現卓越。基于 Transformer 的擴散模型&#xff08;DiT, D…

動態多目標進化算法:VARE(Vector Autoregressive Evolution)求解DF1-DF14,提供完整MATLAB代碼

一、VARE簡介 VARE&#xff08;Vector Autoregressive Evolution&#xff09;算法是2023年提出的一種新型的動態多目標優化&#xff08;DMO&#xff09;算法&#xff0c;旨在有效處理隨時間變化的多目標優化問題。它通過結合向量自回歸&#xff08;VAR&#xff09;模型和環境感…

【JavaEE】-- HTTPS

文章目錄 1. HTTPS是什么&#xff1f;2. 加密是什么&#xff1f;2.1 引入對稱加密&#xff08;效率高&#xff09;2.2 引入非對稱加密&#xff08;效率低&#xff09;2.3 引入證書2.3.1 數據簽名2.3.2 通過證書解決中間人攻擊 1. HTTPS是什么&#xff1f; HTTP也是一個應用層協…

撰寫腳本,通過發布/joint_states話題改變機器人在Rviz中的關節角度

撰寫腳本&#xff0c;通過發布/joint_states話題改變機器人在Rviz中的關節角度 提問 為我寫一個改變關節base_spherical_center_high_joint角度的python腳本吧。適用于ROS2的humble 回答 下面是一個適用于 ROS 2 Humble 的 Python 腳本&#xff0c;它會以指定頻率持續發布 …

Redis : Hash類型

哈希類型 哈希類型的命令 hset 設置key指定的字段&#xff08;field&#xff09;的值&#xff08;value&#xff09; 也可以設置多組field和value hget 獲得哈希中指定字段的值 如果該字段為空或者key為空則返回nil hexists 判斷哈希中是否有該字段的值 如果有則會返回1…

【Chipyard】 conda 環境安裝與使用

一、Conda 安裝&#xff08;以 Miniconda 為例&#xff09; Conda 是一個強大的環境管理工具&#xff0c;可用于創建、隔離和管理不同的 Python/R 環境。以下是在 Ubuntu 上安裝和使用的完整流程&#xff1a; 1. 下載 Miniconda 安裝包 bash # 下載最新版 Miniconda3&#…

【愚公系列】《生產線數字化設計與仿真》009-顏色分類站仿真(設置顏色分類站的仿真序列)

&#x1f31f;【技術大咖愚公搬代碼&#xff1a;全棧專家的成長之路&#xff0c;你關注的寶藏博主在這里&#xff01;】&#x1f31f; &#x1f4e3;開發者圈持續輸出高質量干貨的"愚公精神"踐行者——全網百萬開發者都在追更的頂級技術博主&#xff01; &#x1f…

ios 26官宣:car play升級提升車載體驗

蘋果公司正式官宣推出ios 26&#xff0c;該版本為ios 18的后續版本。此次軟件更新在命名上煥然一新&#xff0c;設計層面更是實現了全面革新。其中&#xff0c;car play界面迎來全新升級&#xff0c;同時還新增了car play ultra功能。借助這一功能&#xff0c;用戶不僅能夠使用…

Mac 上使用 mysql -u root -p 命令,出現“zsh: command not found: mysql“?如何解決

一、確定 MySQL 安裝路徑&#xff1a; 如果你是使用 Homebrew 安裝的 MySQL&#xff0c;通常安裝路徑是 /usr/local/mysql/bin 如果你是通過官方 DMG 安裝包安裝的 MySQL&#xff0c;默認安裝路徑可能是 /usr/local/mysql/bin 你可以在終端中使用以下命令來查找 MySQL 的安裝…

微軟PowerBI考試 PL300-使用適用于 Power BI 的 Copilot 創建交互式報表

微軟PowerBI考試 PL300-使用適用于 Power BI 的 Copilot 創建交互式報表 Microsoft Power BI 可幫助您通過交互式報表準備數據并對數據進行可視化。 如果您是 Power BI 的新用戶&#xff0c;可能很難知道從哪里開始&#xff0c;并且創建報表可能很耗時。 通過適用于 Power BI …

Android11三網共存

一、優先級基本知識介紹 Android6.0之后系統中優先級設置都是根據Score分值來設置優先級&#xff0c;分值0-100&#xff0c;數值越高&#xff0c;越優先。 SIM卡網絡 50 wifi網絡 60 有線網絡 70手機網絡設置都有自己的Factory設置類&#xff0c;都繼承自NetworkFactory.j…

【散刷】二叉樹基礎OJ題(三)

&#x1f4dd;前言說明&#xff1a; 本專欄主要記錄本人的基礎算法學習以及刷題記錄&#xff0c;使用語言為C。 每道題我會給出LeetCode上的題號&#xff08;如果有題號&#xff09;&#xff0c;題目&#xff0c;以及最后通過的代碼。沒有題號的題目大多來自牛客網。對于題目的…

什么是數據交換?有哪些數據交換方式?

目錄 一、數據交換是什么 二、數據交換面臨的挑戰 1. 數據格式差異 2. 數據標準不統一 3. 安全與隱私問題 4. 網絡與性能問題 三、常見的數據交換方式 1. 文件交換 2. 數據庫直連 3. 中間件交換 4. API接口交換 四、數據交換的發展趨勢 1. 實時性要求提高 2. 標準…

C#winform畫圖代碼記錄

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;namespace 坐標變換 {public partial class Fo…

python打卡day50

import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pyplot as plt import numpy as np# 定義通道注意力 class ChannelAttention(nn.Module):def __i…

Go語言多線程問題

打印零與奇偶數&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥鎖和條件變量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…