新聞中心
?前言

有多種方法可以在代碼中定義顏色。最常用的方法是指定三種基色的值 - 紅色、綠色和藍(lán)色 (RGB)。本文通過(guò)指定色調(diào)、飽和度和亮度 (HSB) 的值來(lái)探索替代機(jī)制的使用??梢砸愿庇^的方式使用 HSB 屬性來(lái)創(chuàng)建顏色搭配良好的調(diào)色板。
網(wǎng)上有很多關(guān)于顏色的資源,我發(fā)現(xiàn) Jonathan 的 Learn about Hue, Saturation and Brightness colours[1] 以及 Erik Kennedy 的 The HSB Color System: A Practitioner's Primer[2] 特別有用。
RGB 顏色
RGB 顏色 (紅色、綠色 & 藍(lán)色)
定義顏色的最常見(jiàn)方法是指定顏色的紅色、綠色和藍(lán)色屬性。每個(gè)屬性可以是 0 到 255 之間的十進(jìn)制值,但通常以十六進(jìn)制格式給出,因此顏色可以用 6 個(gè)字符表示。Mac 上的 數(shù)碼測(cè)色計(jì) 可用于檢查屏幕上的任何區(qū)域并給出所選顏色的 RGB 值。可以在 SwiftUI 中創(chuàng)建一個(gè)調(diào)色板以顯示可能的顏色。
struct RgbColorPaletteView: View {
var body: some View {
VStack(spacing:5) {
VStack {
HStack {
Text("Red")
.frame(width: cellWidth)
Text("Green")
.frame(width: cellWidth * 11.0)
Text("Blue")
.frame(width: cellWidth)
Spacer()
}
HStack(spacing:1) {
Spacer()
.frame(width:cellWidth)
ForEach([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], id: \.self) { myGreen in
Text("\(myGreen, specifier: "%0.1F")")
.font(.footnote)
.multilineTextAlignment(.center)
.frame(width:cellWidth)
}
Spacer()
}
}
ForEach([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], id: \.self) { myRed in
HStack(spacing:1) {
Text("\(myRed, specifier: "%0.1F")")
.frame(width:cellWidth)
ForEach([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], id: \.self) { myGreen in
HStack {
VStack(spacing:1) {
ForEach([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], id: \.self) { myBlue in
Color(red: myRed, green: myGreen, blue: myBlue)
}
.frame(width:cellWidth)
}
}
}
VStack(spacing:13) {
Text(myRed == 0.0 ? "0.0" : "")
Image(systemName: "arrow.down")
.foregroundColor(myRed == 0.0 ? Color.black : .clear)
Text(myRed == 0.0 ? "1.0" : "")
}
.font(.footnote)
.frame(width:cellWidth * 0.7)
Spacer()
}
}
Spacer()
}
.padding()
}
let cellWidth: CGFloat = 100
}
HSB 顏色
HSB 顏色(色調(diào)、飽和度 & 亮度)
HSB 顏色模型被認(rèn)為更符合我們對(duì)顏色的看法。下面是通過(guò)改變色調(diào)、飽和度和亮度的值來(lái)顯示調(diào)色板的代碼。請(qǐng)注意,色相(Hue) 通常被賦予一個(gè)以角度為單位的值,表示色環(huán)周?chē)慕嵌?,值?0 到 360 之間,SwiftUI 使用 0.0 到 1.0 之間的值,其中 1.0 表示 360 度。
struct HsbColorPaletteView: View {
var body: some View {
VStack(spacing:5) {
VStack {
HStack {
Text("Hue")
.frame(width: cellWidth)
Text("Saturation")
.frame(width: cellWidth * 11.0)
Text("Brightness")
.frame(width: cellWidth)
Spacer()
}
HStack(spacing:1) {
Spacer()
.frame(width:cellWidth)
ForEach([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], id: \.self) { mySat in
Text("\(mySat, specifier: "%0.1F")")
.font(.footnote)
.multilineTextAlignment(.center)
.frame(width:cellWidth)
}
Spacer()
}
}
ForEach([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], id: \.self) { myHue in
HStack(spacing:1) {
Text("\(myHue, specifier: "%0.1F")")
.frame(width:cellWidth)
ForEach([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], id: \.self) { mySat in
HStack {
VStack(spacing:1) {
//ForEach([1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0], id: \.self) { myBright in
ForEach([1.0, 0.9, 0.8, 0.7, 0.6, 0.5], id: \.self) { myBright in
Color(hue: myHue,
saturation: mySat,
brightness: myBright)
}
.frame(width:cellWidth)
}
}
}
VStack(spacing:13) {
Text(myHue == 0.0 ? "1.0" : "")
Image(systemName: "arrow.down")
.foregroundColor(myHue == 0.0 ? Color.black : .clear)
Text(myHue == 0.0 ? "0.5" : "")
}
.font(.footnote)
.frame(width:cellWidth * 0.5)
Spacer()
}
}
Spacer()
}
.padding()
}
let cellWidth: CGFloat = 100
}
色調(diào)、飽和度和亮度
色調(diào):通過(guò)彩虹的顏色代表從紅色到紫色的基色。
飽和度:表示顏色的強(qiáng)度。當(dāng)亮度為 1.0 時(shí),無(wú)論指定的色調(diào)如何,飽和度值為 0 都將是白色。
亮度:表示顏色的亮度或明度。無(wú)論指定的色調(diào)如何,亮度為 0 都將是黑色。
下圖顯示了一個(gè)個(gè)第一行基于色調(diào)增加的不同顏色,第二行和第三行具有相同的色調(diào),分別顯示增加飽和度和亮度的效果??梢酝ㄟ^(guò)將飽和度保持為 0 并調(diào)整亮度來(lái)定義灰度顏色。
struct ChangeHsbView: View {
var body: some View {
ZStack {
Color(hue: 0.58, saturation: 0.17, brightness: 1.0)
.edgesIgnoringSafeArea(.all)
VStack() {
VStack {
Text("Colors defined with")
Text("Hue, Saturation & Brightness")
}
.font(.title)
.fontWeight(.bold)
HueView()
.frame(height:200)
SatView()
.frame(height:200)
BrightView()
.frame(height:200)
Spacer()
}
.padding(.horizontal, 150)
}
}
}
struct HueView: View {
var body: some View {
VStack(alignment: .leading) {
Text("1. Hue changes the Color")
.font(.title2)
HStack {
ForEach([0.0, 0.2, 0.4, 0.6, 0.8, 1.0], id: \.self) { myHue in
VStack {
RoundedRectangle(cornerRadius: 20)
.fill(Color(hue: myHue, saturation: 1.0, brightness: 1.0))
.shadow(radius: 3, x:5, y:5)
Text("H: \(myHue, specifier: "%0.2F")")
.foregroundColor(.red)
Text("S: 1.00")
Text("B: 1.00")
}
}
}
}
.padding(.vertical, 20)
}
}
struct SatView: View {
var body: some View {
VStack(alignment: .leading) {
Text("2. Saturation changes color Intensity")
.font(.title2)
HStack {
ForEach([0.0, 0.2, 0.4, 0.6, 0.8, 1.0], id: \.self) { mySat in
VStack {
RoundedRectangle(cornerRadius: 20)
.fill(Color(hue: 0.75, saturation: mySat, brightness: 1.0))
.shadow(radius: 3, x:5, y:5)
Text("H: 0.75")
Text("S: \(mySat, specifier: "%0.2F")")
.foregroundColor(.red)
Text("B: 1.00")
}
}
}
}
.padding(.vertical, 20)
}
}
struct BrightView: View {
var body: some View {
VStack(alignment: .leading) {
Text("3. Brightness changes whiteness")
.font(.title2)
HStack {
ForEach([0.0, 0.2, 0.4, 0.6, 0.8, 1.0], id: \.self) { myBright in
VStack {
RoundedRectangle(cornerRadius: 20)
.fill(Color(hue: 0.75, saturation: 1.00, brightness: myBright))
.shadow(radius: 3, x:5, y:5)
Text("H: 0.75")
Text("S: 1.00")
Text("B: \(myBright, specifier: "%0.2F")")
.foregroundColor(.red)
}
}
}
}
.padding(.vertical, 20)
}
}
色輪
在 HSB 顏色模型中,色調(diào)表示基色,可以通過(guò)圍繞色環(huán)的角度(以度為單位)來(lái)指定,其中紅色位于頂部,顏色沿順時(shí)針?lè)较蚋S彩虹的顏色。SwiftUI 使用 0 到 1 之間的值來(lái)表示從 0 到 360 度的色調(diào)值。以下代碼在類(lèi)似于在在 SwiftUI 中創(chuàng)建一個(gè)環(huán)形 Slider中的環(huán)形 Slider 用于顯示色調(diào)選項(xiàng)。移動(dòng)滑塊可選擇色調(diào),所選色調(diào)會(huì)顯示不同的飽和度和亮度值。
struct ColorWheelView: View {
@State private var hue: Double = 180.0
var body: some View {
ZStack {
Color(hue: 0.58, saturation: 0.17, brightness: 1.0)
.edgesIgnoringSafeArea(.all)
VStack(spacing: 40) {
VStack(spacing: 5) {
Text("Select Hue")
.font(.system(size: 40, weight: .bold, design:.rounded))
HStack {
CircularSliderView(value: $hue, in: 0...360)
.frame(width: 300, height: 300)
}
}
VStack(spacing: 5) {
Text("Selected Hue with decreasing Saturation")
.font(.system(size: 40, weight: .bold, design:.rounded))
HStack() {
ForEach([1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0], id: \.self) { mySat in
VStack {
RoundedRectangle(cornerRadius: 10)
.fill(Color(hue: hue/360,
saturation: mySat,
brightness: 1.0))
.frame(height:100)
.overlay {
Text("\(mySat, specifier: "%0.1F")")
.font(.system(size: 30))
}
Text("H: \(hue/360, specifier: "%0.2F")")
Text("S: \(mySat, specifier: "%0.2F")")
Text("B: 1.00")
}
}
}
.padding(.horizontal, 100)
}
VStack(spacing: 5) {
Text("Selected Hue with decreasing Brightness")
.font(.system(size: 40, weight: .bold, design:.rounded))
HStack() {
ForEach([1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0], id: \.self) { myBright in
VStack {
RoundedRectangle(cornerRadius: 10)
.fill(Color(hue: hue/360,
saturation: 1.0,
brightness: myBright))
.frame(height:100)
.overlay {
Text("\(myBright, specifier: "%0.1F")")
.font(.system(size: 30))
.foregroundColor(myBright > 0.5 ? Color.black : .white)
}
Text("H: \(hue/360, specifier: "%0.2F")")
Text("S: 1.00")
Text("B: \(myBright, specifier: "%0.2F")")
}
}
}
.padding(.horizontal, 100)
}
Spacer()
}
}
}
}
struct CircularSliderView: View {
@Binding var progress: Double
@State private var rotationAngle = Angle(degrees: 0)
private var minValue = 0.0
private var maxValue = 1.0
init(value progress: Binding, in bounds: ClosedRange = 0...1) {
self._progress = progress
self.minValue = Double(bounds.first ?? 0)
self.maxValue = Double(bounds.last ?? 1)
self.rotationAngle = Angle(degrees: progressFraction * 360.0)
}
var body: some View {
GeometryReader { gr in
let radius = (min(gr.size.width, gr.size.height) / 2.0) * 0.9
let sliderWidth = radius * 0.3
VStack(spacing:0) {
ZStack {
Circle()
.strokeBorder(hueAngularGradient,
style: StrokeStyle(lineWidth: sliderWidth))
.rotationEffect(Angle(degrees: -90))
.overlay() {
Text("\(progress, specifier: "%.0f")")
.font(.system(size: radius * 0.5, weight: .bold, design:.rounded))
}
Circle()
.fill(Color.white)
.shadow(radius: (sliderWidth * 0.3))
.frame(width: sliderWidth, height: sliderWidth)
.offset(y: -(radius - (sliderWidth * 0.5)))
.rotationEffect(rotationAngle)
.gesture(
DragGesture(minimumDistance: 0.0)
.onChanged() { value in
changeAngle(location: value.location)
}
)
}
.frame(width: radius * 2.0, height: radius * 2.0, alignment: .center)
.padding(radius * 0.1)
}
.onAppear {
self.rotationAngle = Angle(degrees: progressFraction * 360.0)
}
}
}
private var progressFraction: Double {
return ((progress - minValue) / (maxValue - minValue))
}
private func changeAngle(location: CGPoint) {
// Create a Vector for the location (reversing the y-coordinate system on iOS)
let vector = CGVector(dx: location.x, dy: -location.y)
// Calculate the angle of the vector
let angleRadians = atan2(vector.dx, vector.dy)
// Convert the angle to a range from 0 to 360 (rather than having negative angles)
let positiveAngle = angleRadians < 0.0 ? angleRadians + (2.0 * .pi) : angleRadians
// Update slider progress value based on angle
progress = ((positiveAngle / (2.0 * .pi)) * (maxValue - minValue)) + minValue
rotationAngle = Angle(radians: positiveAngle)
}
let hueAngularGradient = AngularGradient(
gradient: Gradient(colors: [
Color(hue: 0.0, saturation: 1.0, brightness: 1.0),
Color(hue: 0.1, saturation: 1.0, brightness: 1.0),
Color(hue: 0.2, saturation: 1.0, brightness: 1.0),
Color(hue: 0.3, saturation: 1.0, brightness: 1.0),
Color(hue: 0.4, saturation: 1.0, brightness: 1.0),
Color(hue: 0.5, saturation: 1.0, brightness: 1.0),
Color(hue: 0.6, saturation: 1.0, brightness: 1.0),
Color(hue: 0.7
網(wǎng)頁(yè)題目:使用HSB而不是RGB來(lái)定義顏色
標(biāo)題路徑:http://www.dlmjj.cn/article/dhgidid.html


咨詢(xún)
建站咨詢(xún)
