整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

TaskBuilder用echarts第三方水球插件实现动态水球波纹图表的方法

/准备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

  1. 打开软件 新建一个页面 添加一个容器组件 用于绑定图表对象 组件名称随便填写 后面要用到

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 } }] };

形状

水填充图表的形状。有可能:

  • //默认形状:'circle',圆形 'rect'正方形, 'roundRect'圆角正方形, 'triangle'三角形, 'diamond'菱形, 'pin'气球形, 'arrow'多边三角形;
  • 'container':完全填满容器的形状。
  • 'path://'以.开头的 SVG 路径
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. 当我们把它当作参数传递颜色的时候,可能不是很明显地表示这个名字是一种颜色

enum 登場

小伙伴这个时候想说~ 哼哼~ 我自定义一个类型不就可以解决了吗?

这个时候肯定已经在想封装一个 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

}

现在我们拥有一个一流的订制类型的好处~享受它吧

Associated Values 相關值

ColorName 对于颜色的命名~

当然是好的,但是 CSS 里面到底有多少种颜色表示:named, RGB, HSL 等等~

我们又要怎么模拟这些呢?

Swift 中的枚举非常适合用于建模具有多种表现形式的东西,比方说 CSS 的颜色,每个枚举都可以有自己的数据配对,这些数据就叫做关联值

CSSColor 通過將他添加近來定義使用枚舉:

enum CSSColor {

case named(ColorName)

case rgb(UInt8, UInt8, UInt8)

}

通过这个定义,我们可以给 CSSColor 模型两种状态。

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 的用法,这三者都有关键的相似之处,他们都可以封装,都可以初始化,也具有计算属性,可以采用协议,并且追溯模型

但是他们也是有重要的区别的!

  • 枚举是具有一个组案例的值类型,每种情况都可以具有不同的参考值。

  • 枚举类型的每个直表示枚举定义的单个大小,他们不能储存任何属性。

  • 結構體是值類型,也可以具有存儲的屬性。

  • 類,他也可以存儲,並切他們可以被建構到覆蓋屬性和方法類的結構層次中,因次 基類的初始化是一個需求,但是和其他人不同,類可以引用,也就是共享。

希望今天的分享對你有幫助~