/準備echarts腳本文件
官網下載最新版本:https://echarts.apache.org/zh/download.html
開源庫下載:https://cdn.jsdelivr.net/npm/echarts@5.3.3/dist/echarts.min.js
//5.33改成最新版本的版本號 就可以下載最新版本了
下載第三方水球庫插件:https://github.com/ecomfe/echarts-liquidfill
2.添加一個事件來加載圖表腳本,我用的的頁面加載時 ,也可以添加后臺服務組件,通過后臺服務組件事件:服務響應時來調用腳本 可以很方便的綁定數據
3.添加剛才下載echarts腳本文件
添加第三方水球插件:
引入腳本
要創建液體填充圖表,您需要有一個類型為'liquidFill'. 一個基本選項可能是:
option = { series: [{ type: 'liquidFill', data: [0.6] }] };
很容易創建一個帶有多個波浪的液體填充圖表,或者表示多個數據,或者提高圖表的視覺效果。
option = { series: [{ type: 'liquidFill', data: [0.6, 0.5, 0.4, 0.3]}]};
這將在 60%、50%、40% 和 30% 的位置創建一個帶有波浪的圖表。
要為液體填充圖表系列設置顏色,請設置color為顏色數組。要設置不透明度,請使用itemStyle.opacity和itemStyle.emphasis.opacity用于普通樣式和懸停樣式。
option = { series: [{ type: 'liquidFill', data: [0.5, 0.4, 0.3], color: ['red', '#0f0', 'rgb(0, 0, 255)'], itemStyle: { opacity: 0.6 }, emphasis: { itemStyle: { opacity: 0.9 } } }] };
您還可以通過以下方式設置單個數據項的顏色和不透明度:
option = { series: [{ type: 'liquidFill', data: [0.5, 0.4, { value: 0.3, itemStyle: { color: 'red', opacity: 0.6 }, emphasis: { itemStyle: { opacity: 0.9 } } }] }] };
為了防止波浪向左或向右移動,您可以簡單地設置waveAnimation為false。要禁用波浪上升的動畫,請將animationDuration和設置animationDurationUpdate為 0。
option = { series: [{ type: 'liquidFill', waveAnimation: false, animationDuration: 0, animationDurationUpdate: 0, data: [0.6, 0.5, 0.4, 0.3] }] };
您可以將 設置amplitude為 0 以產生靜止波。
option = { series: [{ type: 'liquidFill', data: [0.6, 0.5, 0.4, 0.3], amplitude: 0, waveAnimation: 0 }] };
在這種情況下建議設置waveAnimation為 false 以禁用動畫以考慮性能。
要更改單個波形,請覆蓋數據項中的選項。
option = { series: [{ type: 'liquidFill', data: [0.6, { value: 0.5, direction: 'left', itemStyle: { color: 'red' } }, 0.4, 0.3] }] };
您可以使用 backgroundStyle 選項來設置背景形狀的筆觸、填充樣式。
option = { series: [{ type: 'liquidFill', data: [0.6, 0.5, 0.4, 0.3], backgroundStyle: { borderWidth: 5, borderColor: 'red', color: 'yellow' } }] };
要隱藏輪廓,只需設置outline.show為false。
option = { series: [{ type: 'liquidFill', data: [0.6, 0.5, 0.4, 0.3], outline: { show: false } }] };
水填充圖表的形狀。有可能:
options = [{ series: [{ type: 'liquidFill', data: [0.6, 0.5, 0.4, 0.3], shape: 'diamond' }] }];
option = {
series: [{
type: 'liquidFill',
data: [0.5, 0.4, 0.3, 0.2],
shape: 'container',
outline: {
show: false
}
}]
};
option = { series: [{ type: 'liquidFill', data: [0.6, 0.55, 0.4, 0.25], radius: '60%', outline: { show: false }, backgroundStyle: { borderColor: '#156ACF', borderWidth: 1, shadowColor: 'rgba(0, 0, 0, 0.4)', shadowBlur: 20 }, shape: 'path://M367.855,428.202c-3.674-1.385-7.452-1.966-11.146-1.794c0.659-2.922,0.844-5.85,0.58-8.719 c-0.937-10.407-7.663-19.864-18.063-23.834c-10.697-4.043-22.298-1.168-29.902,6.403c3.015,0.026,6.074,0.594,9.035,1.728 c13.626,5.151,20.465,20.379,15.32,34.004c-1.905,5.02-5.177,9.115-9.22,12.05c-6.951,4.992-16.19,6.536-24.777,3.271 c-13.625-5.137-20.471-20.371-15.32-34.004c0.673-1.768,1.523-3.423,2.526-4.992h-0.014c0,0,0,0,0,0.014 c4.386-6.853,8.145-14.279,11.146-22.187c23.294-61.505-7.689-130.278-69.215-153.579c-61.532-23.293-130.279,7.69-153.579,69.202 c-6.371,16.785-8.679,34.097-7.426,50.901c0.026,0.554,0.079,1.121,0.132,1.688c4.973,57.107,41.767,109.148,98.945,130.793 c58.162,22.008,121.303,6.529,162.839-34.465c7.103-6.893,17.826-9.444,27.679-5.719c11.858,4.491,18.565,16.6,16.719,28.643 c4.438-3.126,8.033-7.564,10.117-13.045C389.751,449.992,382.411,433.709,367.855,428.202z', label: { position: ['38%', '40%'], formatter: function() { return 'ECharts\nLiquid Fill'; }, fontSize: 40, color: '#D94854' } }] };
一般來說,液體填充圖表中有兩種類型的動畫。
第一種是初始動畫,具有升浪的效果。此動畫的緩動方法由 控制,animationEasing其持續時間由控制animationDuration。
第二種是更新動畫,通常在數據變化、波高變化時使用。它們由animationEasingUpdate和控制animationDurationUpdate。
例如,要禁用提升動畫并將更新動畫時間設置為 2 秒cubicOut,可以使用以下選項:
var mytubiao = echarts.init(document.getElementById("mytb"))
option = {
series: [{
type: 'liquidFill',
data: [0.6, 0.5, 0.4, 0.3],
animationDuration: 0,
animationDurationUpdate: 2000,
animationEasingUpdate: 'cubicOut'
}]
};
mytubiao.setOption(option);
setTimeout(function () {
mytubiao.setOption({
series: [{
type: 'liquidFill',
data: [0.8, 0.6, 0.4, 0.2]
}]
})
}, 3000);
默認情況下,液體填充圖表的文本標簽顯示第一個數據的百分比。例如,對于帶有 data 的圖表[0.6, 0.5, 0.4, 0.3],默認文本是60%.
要更改文本,您可以使用label.formatter,它可以設置為字符串或函數。
如果是字符串,{a}則表示系列名稱、{b}數據名稱和{c}數據值。
option = { series: [{ type: 'liquidFill', name: 'Liquid Fill', data: [{ name: 'First Data', value: 0.6 }, 0.5, 0.4, 0.3], label: { formatter: '{a}\n{b}\nValue: {c}', fontSize: 28 } }] };
此示例的標簽文本為'Liquid Fill\nFirst Data\nValue: 0.6'.
formatter這與作為函數使用的結果相同:
option = { series: [{ type: 'liquidFill', name: 'Liquid Fill', data: [{ name: 'First Data', value: 0.6 }, 0.5, 0.4, 0.3], label: { formatter: function(param) { return param.seriesName + '\n' + param.name + '\n' + 'Value:' + param.value; }, fontSize: 28 } }] };
文本位置默認在中心。label.position可以設置為'inside', 'left', 'right', 'top', 'bottom', 或水平和垂直位置,例如['10%', '20%'],表示'10%'向左(由 控制label.align,可以是'left'、'center'或'right')和'20%'頂部(由 控制label.baseline,可以是'top'、'middle'或'bottom')。
陰影
默認情況下,波浪和輪廓上有陰影。以下是如何更改它們。
option = { series: [{ type: 'liquidFill', data: [0.6, 0.5, 0.4, 0.3], itemStyle: { shadowBlur: 0 }, outline: { borderDistance: 0, itemStyle: { borderWidth: 5, borderColor: '#156ACF', shadowBlur: 20, shadowColor: 'rgba(255, 0, 0, 1)' } } }] };
添加工具提示:
option = { series: [{ type: 'liquidFill', data: [0.6], name: 'Liquid Fill' }], tooltip: { show: true } };
要在 wave 上添加點擊事件:
chart.setOption(option); chart.on('click', function() { console.log(arguments); // do something useful here });
與任何其他圖表類型一樣,上述代碼只會觸發波事件。如果要跟蹤整個畫布或特定元素上的事件,可以將偵聽器添加到 zrender,例如:
chart.getZr().on('click', function() { console.log(arguments); });
不可交互
要使元素(例如,波浪)不可交互,只需設置silent為true.
option = { series: [{ type: 'liquidFill', data: [0.6, 0.5, 0.4, 0.3], silent: true }] };
API
液體填充圖表的默認選項是:
{
data: [],
color: ['#294D99', '#156ACF', '#1598ED', '#45BDFF'],
center: ['50%', '50%'],
radius: '50%',
amplitude: '8%',
waveLength: '80%',
phase: 'auto',
period: 'auto',
direction: 'right',
shape: 'circle',
waveAnimation: true,
animationEasing: 'linear',
animationEasingUpdate: 'linear',
animationDuration: 2000,
animationDurationUpdate: 1000,
outline: {
show: true,
borderDistance: 8,
itemStyle: {
color: 'none',
borderColor: '#294D99',
borderWidth: 8,
shadowBlur: 20,
shadowColor: 'rgba(0, 0, 0, 0.25)'
}
},
backgroundStyle: {
color: '#E3F7FF'
},
itemStyle: {
opacity: 0.95,
shadowBlur: 50,
shadowColor: 'rgba(0, 0, 0, 0.4)'
},
label: {
show: true,
color: '#294D99',
insideColor: '#fff',
fontSize: 50,
fontWeight: 'bold',
align: 'center',
baseline: 'middle'
position: 'inside'
},
emphasis: {
itemStyle: {
opacity: 0.8
}
}
}
數據{(數字|對象)[]}
每個數據項的值應介于 0 和 1 之間。
數據項也可以是配置單個項的選項的對象。
option = {
series: [{
type: 'liquidFill',
data: [0.6, {
value: 0.5,
itemStyle: {
color: 'red'
}
}, 0.4, 0.3]
}]
};
這定義了具有第二波紅色的圖表。
顏色 {string[]}
波浪顏色。
形狀 {字符串}
水填充圖表的形狀。它可以是默認符號之一:'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow'. 或者,以 . 開頭的 SVG 路徑'path://'。
中心{字符串[]}
圖表的位置。第一個值是 x 位置,第二個值是 y 位置。每個值都可以是一個相對值,例如'50%',它是相對于容器寬度和高度的較小值的,也可以是一個絕對值,例如100px。
半徑 {字符串}
圖表的半徑,可以是相對值,如'50%',相對于容器寬度和高度的較小值,也可以是絕對值,如100px。
幅度{數}
波的幅度,以像素或百分比為單位。如果它是百分比,它是相對于直徑的。
波長 {字符串|數字}
波的波長,可以是相對值,例如'50%',它是相對于直徑的,也可以是絕對值,例如'100px'或100。
階段{編號}
波的相位,以弧度表示。默認情況下'auto',當每個波的相位Math.PI / 4大于前一個時,它被設置為 。
周期 {number|'auto'|function}
向前移動一個波長所需的毫秒數。默認情況下,'auto'當前面的波速度較大時,它被設置為 。
它也可以是格式化程序功能。
option = {
series: [{
type: 'liquidFill',
data: [0.6, 0.5, 0.4, 0.3],
radius: '70%',
phase: 0,
period: function (value, index) {
// This function is called four times, each for a data item in series.
// `value` is 0.6, 0.5, 0.4, 0.3, and `index` is 0, 1, 2, 3.
return 2000 * index + 1000;
}
}]
}
方向 {字符串}
波浪移動的方向,應該是'right'或'left'。
waveAnimation {boolean}
是否啟用向左或向右移動的波浪。
動畫緩動 {字符串}
初始動畫的緩動方法,當波浪從底部開始上升時。
animationEasingUpdate {字符串}
其他動畫的緩動方法,例如,當數據值改變和波浪位置改變時。
動畫持續時間 {數字}
初始動畫持續時間,以毫秒為單位。
animationDurationUpdate {數字}
其他動畫持續時間,以毫秒為單位。
大綱.show {布爾}
是否顯示輪廓。
大綱.borderDistance {number}
邊界和內圈之間的距離。
outline.itemStyle.borderColor {字符串}
邊框顏色。
outline.itemStyle.borderWidth {number}
邊框寬度。
outline.itemStyle.shadowBlur {number}
輪廓陰影模糊大小。
outline.itemStyle.shadowColor {字符串}
輪廓陰影顏色。
backgroundStyle.color {字符串}
背景填充顏色。
backgroundStyle.borderWidth {字符串}
背景描邊線寬。
backgroundStyle.borderColor {字符串}
背景描邊線寬。
backgroundStyle.itemStyle.shadowBlur {number}
背景陰影模糊大小。
backgroundStyle.itemStyle.shadowColor {字符串}
背景陰影顏色。
backgroundStyle.itemStyle.opacity {number}
背景不透明度。
itemStyle.opacity {number}
波浪不透明度。
itemStyle.shadowBlur {number}
波浪陰影寬度。
itemStyle.shadowColor {字符串}
波浪陰影顏色。
強調.itemStyle.opacity {number}
懸停時波浪不透明度。
標簽.show {布爾}
是否顯示標簽文本。
label.color {字符串}
在背景上顯示時文本的顏色。
label.insideColor {字符串}
在波形上顯示時文本的顏色。
label.fontSize {數字}
標簽字體大小。
標簽.fontWeight {字符串}
標簽字體粗細。
標簽對齊{字符串}
文本對齊,應該是'left', 'center', 或'right'.
label.baseline {字符串}
文本垂直對齊,應為'top'、'middle'或'bottom'。
標簽位置 {string|string[]}
文本位置默認在中心。label.position可以設置為'inside', 'left', 'right', 'top', 'bottom', 或水平和垂直位置,例如['10%', '20%'],表示'10%'左側和'20%'頂部。
需要JavaScript,也不需要圖片,只用純CSS就能實現多種炫酷的Loading特效!我這次創作的兩個圓形Loading特效,大小不斷變換,就像是在呼吸一樣,讓人忍不住一直盯著它看。如果你也想要讓你的網站更加吸引人,不妨試試用純CSS來實現自己的Loading特效吧!
經在只有 Objective-C. 的日子里,封裝只能使用類~(真是累。。。)
然而現在 Swift 中有三個選擇——枚舉,結構,類
結合協議 ,這些類型看以發灰經人的威力,雖然他們有很多共同的能力。但是他們也有很多重要的區別。
那么今天跟大家分享的是
● 枚舉,結構,類的使用經驗
● 關于如何使用他們的直覺
● 了解他們的工作原理
根據這些先決條件,Vergil. 在這邊假設大家有一些 Swift 基礎還有面向對象的編成經驗(只有 Objective-C 基礎也可以唷)
一切都跟這些類型有關。
Swift 三大賣點就是 安全 快速 簡易,安全意味著很少會因為意外的方式運行錯誤的代碼,破壞內存并產生難以發現的錯誤。 Seift 會在編譯的時候顯示問題,而不是在運行的時候顯示出來,從而是其變的更明顯。
另外,Swift. 可以清楚的表達意圖,因此可以幫助你的代碼快速運行。
Swift 語言的核心就是簡單還和高度的規范化,這是建立在驚人的少量概念上的。盡管規則比較簡單,但是還是可以用它做出驚人的事情,食蟹這一目標的關鍵就是 Swift. 系統
Swift 類型是強大的~盡管只有六個。沒錯。
不像其他語言多達十幾種內置類型的語言。
Swift. 只有六種。他們分別是:協議,枚舉,結構體,類。另外兩個則是復合類型:元祖,函數。
其他你能想到的基本類型,比方說 Bool,Int,UInt,Float,Double,Character,String,Array,Set,Dictionary,Optional,等
那么今天我們的重點就放在 enum, struct 還有 class.
————————
這邊有段 HTML5 的代碼,然後我們的目標是要實現這個效果
<!DOCTYPE html><html><body><svg width='250' height='250'><rect x='110.0' y='10.0' width='100.0' height='130.0' stroke='teal' fill='aqua' stroke-width='5' /><circle cx='80.0' cy='160.0' r='60.0' stroke='red' fill='yellow' stroke-width='5' /></svg></body></html>
那麼我們就需要一個顏色來表示啦~ 在 SVG 中是可以使用指定名稱來表示 RGB 類型的。
如果說要在 SVG. 中使用顏色,那麼我們就要指定某一個圖形的一部分屬性,比方說 fill = 'gray' 這樣的一個簡單的方式在 swfit 就是幾這個 String 做一個轉換
let fill = 'gary'
但是他也有明顯的缺點的~
1. 任何不屬于顏色的文字都會被運行~比方說 ‘grayy’,’grey’ 之類的,很多時候我們自己都不知道錯在哪里了~
2. 智能提示不會提示 String, 這讓人很蛋疼 Xcode. 稱霸世界的就是他的智能提示了
3. 當我們把它當作參數傳遞顏色的時候,可能不是很明顯地表示這個名字是一種顏色
小伙伴這個時候想說~ 哼哼~ 我自定義一個類型不就可以解決了嗎?
這個時候肯定已經在想封裝一個 UIColor. 的類。但是這個時候~他真的是唯一的選擇嗎?
小伙伴們可能想要這樣子實現~
enum ColorName: String {
case black = "black"
case silver = "silver"
case gray = "gray"
case white = "white"
case maroon = "maroon"
case red = "red"
// ... and so on ...
}
這種很類似 C.的風格~ 也是 Objective-C小伙伴的反射思考,但是 Swfit. 不太一樣,Swift 可以選定一種類型來表示每種情況~
什么意思呢?就是說我們可以指定一個 backing store 類型的枚舉被叫做 RawRepresentable 因為他們會自動采用 RawRepresentable 協議,因此我們可以指定 ColorNameas 的類型 String 并為每個案例分配一個值
enum ColorName: String {
case black = "black"
case silver = "silver"
case gray = "gray"
case white = "white"
case maroon = "maroon"
case red = "red"
// ... and so on ...
}
但是 String 代表性的枚舉做了特別的事情!如果沒有指定特別的情況,編譯器就會自動使 String. 與案例名稱相同。這意味著我們只需要寫案例的名字。
enum ColorName: String {
case black
case silver
case gray
case white
case maroon
case red
// ... and so on ...
}
當然啦~ 我們也可以透過 , 來區分。減少我們的輸入
enum ColorName: String {
case black, silver, gray, white, maroon, red, purple, fuchsia, green, lime, olive, yellow, navy, blue, teal, aqua
}
現在我們擁有一個一流的訂制類型的好處~享受它吧
ColorName 對于顏色的命名~
當然是好的,但是 CSS 里面到底有多少種顏色表示:named, RGB, HSL 等等~
我們又要怎么模擬這些呢?
Swift 中的枚舉非常適合用于建模具有多種表現形式的東西,比方說 CSS 的顏色,每個枚舉都可以有自己的數據配對,這些數據就叫做關聯值。
CSSColor 通過將他添加近來定義使用枚舉:
enum CSSColor {
case named(ColorName)
case rgb(UInt8, UInt8, UInt8)
}
1. 它可以是 named, 在這種情況下,相關連的數據就是一個 ColorName
2. 它可以是 rgb 在這種情況下,他的相關數據就是 紅 藍 綠 (0-255)的數字
具有枚舉的協議和方法 Protocols and Methods with an Enum
現在我們想要打印出多個實例 CSSColor ,在 Swfit. 中枚舉和大家一樣,也可以使用協議的,很特別吧!我們來試試看使用神奇的協議吧。
extension CSSColor: CustomStringConvertible {
var description: String {
switch self {
case .named(let colorName):
return colorName.rawValue
case .rgb(let red, let green, let blue):
return String(format: "#%02X%02X%02X", red,green,blue)
}
}
}
這個 CSSColor 結合了 CustomStringConvertible,這是告訴 Swift 我們的 CSSColor. 類型,可以轉換成字符串。我們告訴他怎么通過 description 計算屬性。
那么在這邊, self. 切換到底層模型是否為 RGB 類型。在這種情況下,我們可以將顏色轉換成所需要的字符串形式。命名的時候我們只返回字符串名稱,RGB 的時候我們則返回所需要的 紅 藍 綠的數值。
我們來看看吧~
let color1 = CSSColor.named(.red)
let color2 = CSSColor.rgb(0xAA, 0xAA, 0xAA)
print("color1 = \(color1), color2 = \(color2)") // prints color1 = red, color2 = #AAAAAA
一切都在編譯的時候被檢查出來是正確的!是不是比當初我們只用 String. 來顯示顏色好多了呢?
> 注意:雖然我們可以對于 CSSColor. 定義并對他進行修改,但是我們不需要這么做!我們只需要接著去擴展他,并且遵循新的協議就可以了。
> 擴展是很好的!因為它使我們定義符合了特定的協議,在這種狀況下 CustomStringConvertible 需要給 description 定義一個字符串屬性實現一個 getter
就像 Swift 中的類和結構一樣,我們可以往枚舉里面添加自訂義初始化值
extension CSSColor {
init(gray: UInt8) {
self = .rgb(gray, gray, gray)
}
}
然後我們來使用它試試看~
let color3 = CSSColor(gray: 0xaa)
print(color3) // prints #AAAAAA
現在我們比更方便的來創建灰色的色彩了~!
命名的類型可以當作命名空間來保持複雜性的最小化,像我們創造了 ColorName 和 CSSColor,如果我們可以隱藏 ColorName 在 CSSColor 裡面,那不是更好嗎?
extension CSSColor {
enum ColorName: String {
case black, silver, gray, white, maroon, red, purple, fuchsia, green, lime, olive, yellow, navy, blue, teal, aqua
}
}
> 注意:Swift. 里面有一個很大的特點就是,你會發現聲明的順序通常不重要,編譯器會多次掃瞄文件,并將他們編號。不需要像是。C/ C++ /Objective-C 一樣照順序聲明。
枚舉也可以設置單純的命名空間,比方說我們需要用到一個數學常數黃金比例。phi
enum Math {
static let phi = 1.6180339887498948482 // golden mean
}
這個 Math 不包含了任何的 case , 并且在擴展里面添加新的 case 是非法的~這個時候我們就可以在枚舉里面處理了,這個 phi. 是個靜態長涼,不需要實例化,沒當我們需要黃金比例數字的時候可以用簡單的 Math.phi 就可以拿到,而不是記住所有的數字!
到目前為止我們可以知道 枚舉在 Swift. 中比在其他語言中強大的太多太多,因為我們可以將它擴展,可以創建自定義初始化方法,提供命名空間并且封裝相關操作。
那么我們現在已經習慣了 enum 的 CSS. 顏色模塊,這樣很好~ 因為CSS的顏色很好理解,就是遵循 W3C. 規范
枚舉對於眾所週知的事物挑選有很好的作用,比方說一週中的第幾天,硬幣的正反面什麼的,毫無疑問。 Swift. 的可選功能(optionals) 是具有 .none 或是 .some. 的狀態來實現的!
好的,現在我們進入下個模型類型 結構!?
完成了顏色之後,我們希望用戶能夠在 SVC. 中字定義自己喜歡的形狀,所以這個時候使用 enum 就不是一個很好的選擇。
因為新的 enum 不能在裡面擴展方法,所以我們交給 struct. 還有 class 來做~
protocol Drawable {
func draw(with context: DrawingContext)
}
protocol DrawingContext {
func draw(circle: Circle)
// more primitives will go here ...
}
這裡有一個 Drawable 協議 裡面有一個繪製的方法,叫做draw
我們要用它來繪製其他的幾何圖形,比方說圓形,矩形。
我們會發現這個時候他抱錯~
因為我們沒有實現 Circle. 的繪製方法,所以我們就來實現一下吧~
struct Circle : Drawable {
var strokeWidth = 5
var strokeColor = CSSColor.named(.red)
var fillColor = CSSColor.named(.yellow)
var center = (x: 80.0, y: 160.0)
var radius = 60.0
// Adopting the Drawable protocol.
func draw(with context: DrawingContext) {
context.draw(circle: self)
}
}
當然啦~我們還有很多選擇~SVG,HTML5 Canvas,Core Graphics,OpenGL,Metal 都是不錯的選擇,這邊只是跟大家說明他的用法,接下來我們要準備好讓 Circle. 繼承的 Drawable
在一個中struct,您將存儲的屬性組合在一起。在這里您已經實現了以下屬性:
● strokeWidth:行的寬度。
● strokeColor:線條的顏色。
● fillColor:填充圓的顏色。
● center:圓的中心點。
● radius:圓的半徑。
結構體跟類有關鍵的差異,最大的區別就是結構體是 值 類型,而類是引用類型。
值和參考值Value vs. Reference Types
值跟參考值是兩個單獨且不同的實體,典型的值就是一個整數。比方說 Int
var a = 10
var b = a
a = 30 // b仍然具有值10.
a == b
對於 Circle (使用 struct)
var a = Circle()
a.radius = 60.0
var b = a
a.radius = 1000.0 // b.radius仍然具有值60.0
如果我們已經從一個類型創建了 Circle. 那麼他被賦予的就是參考值,表示說他引用了一個底層對象。
使用這個值來創建新對象的時候,會複製這個值,當屎引用類型相同的對象的時候,新的變量會引用相同的對象,這就是 class. 跟 struct 的關鍵差異了!
我們來畫一個矩形吧
struct Rectangle : Drawable {
var strokeWidth = 5
var strokeColor = CSSColor.named(.teal)
var fillColor = CSSColor.named(.aqua)
var origin = (x: 110.0, y: 10.0)
var size = (width: 100.0, height: 130.0)
func draw(with context: DrawingContext) {
context.draw(rectangle: self)
}
}
我們還需要使用 DrawingContext 協議,讓他知道如何去繪製。
protocol DrawingContext {
func draw(_ circle: Circle)
func draw(_ rectangle: Rectangle)
// more primitives would go here ...
}
這兩個方法都是遵循協議的時候要做的,現在我們可以來畫一個具有 SCV. 風格的圖了
final class SVGContext : DrawingContext {
private var commands: [String] = []
var width = 250
var height = 250
// 1
func draw(circle: Circle) {
commands.append("<circle cx='\(circle.center.x)' cy='\(circle.center.y)\' r='\(circle.radius)' stroke='\(circle.strokeColor)' fill='\(circle.fillColor)' stroke-width='\(circle.strokeWidth)' />")
}
// 2
func draw(rectangle: Rectangle) {
commands.append("<rect x='\(rectangle.origin.x)' y='\(rectangle.origin.y)' width='\(rectangle.size.width)' height='\(rectangle.size.height)' stroke='\(rectangle.strokeColor)' fill='\(rectangle.fillColor)' stroke-width='\(rectangle.strokeWidth)' />")
}
var svgString: String {
var output = "<svg width='\(width)' height='\(height)'>"
for command in commands {
output += command
}
output += "</svg>"
return output
}
var htmlString: String {
return "<!DOCTYPE html><html><body>" + svgString + "</body></html>"
}
}
SVGContext 是一個包含了一組私有命令字符串的類,在第一還有第二部份中,我們使用 DrawingContext 協議,繪制方法只是附加一個帶有正確的XML大字符串形式
最後,我們需要一個可以包含許多 Drawable 對象的結構
struct SVGDocument {
var drawables: [Drawable] = []
var htmlString: String {
let context = SVGContext()
for drawable in drawables {
drawable.draw(with: context)
}
return context.htmlString
}
mutating func append(_ drawable: Drawable) {
drawables.append(drawable)
}
}
這裏~ htmlString 是一個計算 SVGDocument 的屬性,創建一個 SVGContext 從上到下完整的 htmlString
顯示SVG
好了~最後我們就要看到效果了~
var document = SVGDocument()
let rectangle = Rectangle()
document.append(rectangle)
let circle = Circle()
document.append(circle)
let htmlString = document.htmlString
print(htmlString)
我們可以看到,成功的整合出了文件~
我們試試看按住。Command-Option-Return 可以在助理編輯器里面看到 web. 的效果
類
到目前為止,我們使用了結構和協議的組合來實現可繪制的模型。
即使現在你不用他,了解這種方法對你還是有幫助的!
那么在代碼中,他看起來是不是有點像是下面的代碼~這邊只是參考,可以不用把他加進去
class Shape {
var strokeWidth = 1
var strokeColor = CSSColor.named(.black)
var fillColor = CSSColor.named(.black)
var origin = (x: 0.0, y: 0.0)
func draw(with context: DrawingContext) { fatalError("not implemented") }
}
class Circle: Shape {
override init() {
super.init()
strokeWidth = 5
strokeColor = CSSColor.named(.red)
fillColor = CSSColor.named(.yellow)
origin = (x: 80.0, y: 80.0)
}
var radius = 60.0
override func draw(with context: DrawingContext) {
context.draw(self)
}
}
class Rectangle: Shape {
override init() {
super.init()
strokeWidth = 5
strokeColor = CSSColor.named(.teal)
fillColor = CSSColor.named(.aqua)
origin = (x: 110.0, y: 10.0)
}
var size = (width: 100.0, height: 130.0)
override func draw(with context: DrawingContext) {
context.draw(self)
}
}
為了讓我們使用面向對象編程更安全,Swift.引入了 override. 關鍵字,他要求你自己去承認你覆寫了東西
他可以防止意外的觸碰到現有的方法,或者不正確的覆蓋你以前的方法。在使用新版的庫的時候了解事情發生了變化,他可是一個救命者啊
盡管如此,這種面向對象的‘分法還是存在一些缺點的,比方說,我們在幾類中實現的, Shape 想要被避免被濫用,他會呼吁 fatalError() 提醒我們需要覆蓋這個方法,不幸的是這個檢查是在運行的時候發生,而不是編譯的時候。
其次 Circle. 還有 Rectangle 必須處理幾類數據的初始化。雖然這是一件很容易的事情,但是為了確保正確性,類的初始化還是可能變成一個有些涉及的過程。
第三,往後會證明基類是一個棘手的東西。
假如說,我們想要添加一個 drawable Line class. 那麼為了與現有的系統協同工作,他必須從中得出。Shape. 這是一個誤區。
基於這點~我們可以重構我們的結構層次,然而項目一但大了,這是一個幾乎不可能的任務,我們甚至可能無法修改基類,而且通常都會有很多問題。
最後,class. 存在一個之前討論過的問題,引用。雖然自動引用計數器(ARC) 大部分的時間都在處理事情,但是我們需要注意不要引入循環飲用,否則最後會導致內存泄露。
如果我們將相同的shape導入到shape數組中,那麼假設當我們有一種形狀的顏色修改為紅色的時候,另一種形狀會令人驚訝的跟著修改。
為什么只使用一個類?
鑑於以上幾點,可能小夥伴就覺得奇怪了,為什麼會永遠想使用僅僅一個類?
對於初學者來說,他們允許你採用成熟且經過測試的框架,比如說。Cocoa和 Cocoa touch
另外,Class. 的確有更重要的用途,比如說 一個大的內存占用,昂貴的復制對象在類中包裝好的候選者, Class 也可以很好的模擬身份。我們可以看到很多 View顯示相同對象的情況。如果該對象被修改,所有的View也反映了模型中的修改。使用值類型,同步更新也會遇到問題~
所以命名的模型類型都可以創建不一定對應儲存屬性的自定義 setter 和 getter.
假如我們想要添加一個直徑的 getter 和 setter. 到我們的 Circle 模型。在現有的 radius 很容易實現
extension Circle {
var diameter: Double {
get {
return radius * 2
}
set {
radius = newValue / 2
}
}
}
在這實現了一個純碎的半徑的計算屬性。當我們想要獲得直徑的時候只要把半徑的長度乘上二就好了~
相同的情況下,我們想要畫出一個圓的面積還有週長的時候可以這樣做,
跟類的使用方法不同,在默認的情況下, Struct. 方法不允許修改,也就是 mutating. 如果我們把屬性聲明為 mutating 的話就看以這樣做了
比方說~我們把這個方法添加到 Circle. 擴展中
我們志個時候在自定義一個 Shift() 移動圓的方法,簡單的說就是改變他的圓心。
但是這會改變 center.x 還有 center.y 所以他會抱錯~
這個時候不要驚慌~ 我們把這個方法前面加上一個 mutating 就可以了!
mutating func shift(x: Double, y: Double) {
center.x += x
center.y += y
}
這個故事告訴我們~ 我們其實是可以改變 struct. 的!
Swift 有一大特點就是 追溯模型(Retroactive Modeling), 簡單的說它允許你擴展模型的行為,就算你沒有源代碼!
這裡有個範例,假設說你是 SVG 開發者,並且希望添加 area 和 perimeter 屬性 to Rectangle
extension Rectangle {
var area: Double {
return size.width * size.height
}
var perimeter: Double {
return 2 * (size.width + size.height)
}
}
以前我們想把方法透過 extenstion. 添加到現有的模型中,現在我們把這些方法都獲做協議
protocol ClosedShape {
var area: Double { get }
var perimeter: Double { get }
}
這是一個官方協議~然后我們就可以告訴圓形還有矩形采用這個協議
extension Circle: ClosedShape {}
extension Rectangle: ClosedShape {}
當然我們也可以定義一過寒數,例如,計算ClosedShape 協議的模型數組,的總周長,
func totalPerimeter(shapes: [ClosedShape]) -> Double {
return shapes.reduce(0) { >return shapes.reduce(0) { $0 + $1.perimeter }< + .perimeter }
}
totalPerimeter(shapes: [circle, rectangle])
————————
好了
我們今天分享了關于 enum. Struct. 還有 Class 的用法,這三者都有關鍵的相似之處,他們都可以封裝,都可以初始化,也具有計算屬性,可以采用協議,并且追溯模型
但是他們也是有重要的區別的!
枚舉是具有一個組案例的值類型,每種情況都可以具有不同的參考值。
枚舉類型的每個直表示枚舉定義的單個大小,他們不能儲存任何屬性。
結構體是值類型,也可以具有存儲的屬性。
類,他也可以存儲,並切他們可以被建構到覆蓋屬性和方法類的結構層次中,因次 基類的初始化是一個需求,但是和其他人不同,類可以引用,也就是共享。
希望今天的分享對你有幫助~
*請認真填寫需求信息,我們會在24小時內與您取得聯系。