/准备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小时内与您取得联系。