关于布局:CSS-Grid-Layout网格布局

CSS 有一些属性常常被用来解决布局问题:如(浮动float、定位postion)这些比拟 hack 的办法常常会给页面遗留下一些问题。 弹性盒子Flexbox是一个十分好的布局工具,网格布局 CSS Grid Layout 是最新、更弱小的布局形式。本文就来简略介绍一下什么是网格布局。 网格布局(CSS Grid Layout)网格布局是二维的布局零碎,和过来罕用的布局形式相比齐全扭转了咱们设计UI的形式。 须要理解的术语Grid Container(网格容器),Grid Item(网格容器子元素)。 Grid Line(网格线),Grid Cell(网格单元格)。 Grid Track(网格轨道),Grid Area(被网格线离开的区域)。 图例参考 页面构造<div class="container"> <div class="item item_a">item_a</div> <div class="item item_b">item_b</div> <div class="item item_c">item_c</div></div>定义一个网格布局的容器.container { display: grid;}设置网格行、列.container { display: grid; grid-template-columns: [first] 40px [line2] 50px [line3] auto [col4-start] 50px [five] 40px [end]; grid-template-rows: [row1-start] 100px [row1-end] 100px [third-line] 100px [last-line];}通过下面的形式就申明了一个 3行5列 的网格容器。 设置网格容器内元素地位.item_a { grid-column-start: 4; grid-column-end: five; grid-row-start: row1-start; grid-row-end: 2; background-color: orange;}.item_b { grid-column-start: 1; grid-column-end: span col4-start; grid-row-start: 3; grid-row-end: span 2; background-color: aqua;}.item_c { grid-column-start: 2; grid-column-end: span 1; grid-row-start: 2; grid-row-end: span 1; background-color: rgb(149, 255, 0);}后果图: ...

September 12, 2022 · 1 min · jiezi

关于布局:前端面试每日-31-第630天

明天的知识点 (2021.01.05) —— 第630天 (我也要出题)[html] 实现两列等宽布局的形式有哪些?[css] Less变量的品种有哪些?[js] 写一个办法实现指定开始的数字减少到指定的完结数字,步长默认为1[软技能] 你有什么喜好?《论语》,曾子曰:“吾日三省吾身”(我每天屡次检查本人)。前端面试每日3+1题,以面试题来驱动学习,每天提高一点!让致力成为一种习惯,让奋斗成为一种享受!置信 保持 的力量!!!欢送在 Issues 和敌人们一起探讨学习! 我的项目地址:前端面试每日3+1【举荐】欢送跟 jsliang 一起折腾前端,零碎整顿前端常识,目前正在折腾 LeetCode,打算买通算法与数据结构的任督二脉。GitHub 地址 微信公众号欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个Star, 同时欢送微信扫码关注 前端剑解 公众号,并退出 “前端学习每日3+1” 微信群互相交换(点击公众号的菜单:交换)。 学习不打烊,充电加油只为遇到更好的本人,365天无节假日,每天早上5点纯手工公布面试题(死磕本人,愉悦大家)。心愿大家在这虚夸的前端圈里,放弃沉着,保持每天花20分钟来学习与思考。在这变幻无穷,类库层出不穷的前端,倡议大家不要等到找工作时,才狂刷题,提倡每日学习!(不忘初心,html、css、javascript才是基石!)欢送大家到Issues交换,激励PR,感激Star,大家有啥好的倡议能够加我微信一起交换探讨!心愿大家每日去学习与思考,这才达到来这里的目标!!!(不要为了谁而来,要为本人而来!)交换探讨欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个[Star]

January 5, 2021 · 1 min · jiezi

关于布局:前端面试每日-31-第626天

明天的知识点 (2021.01.01) —— 第626天 (我也要出题)[html] 写一个程度竖直居中的弹窗,带遮罩层的布局[css] Less文件的扩展名是什么?[js] 写一个办法动静同步加载script文件[软技能] UTC工夫和GMT有什么区别?《论语》,曾子曰:“吾日三省吾身”(我每天屡次检查本人)。前端面试每日3+1题,以面试题来驱动学习,每天提高一点!让致力成为一种习惯,让奋斗成为一种享受!置信 保持 的力量!!!欢送在 Issues 和敌人们一起探讨学习! 我的项目地址:前端面试每日3+1【举荐】欢送跟 jsliang 一起折腾前端,零碎整顿前端常识,目前正在折腾 LeetCode,打算买通算法与数据结构的任督二脉。GitHub 地址 微信公众号欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个Star, 同时欢送微信扫码关注 前端剑解 公众号,并退出 “前端学习每日3+1” 微信群互相交换(点击公众号的菜单:交换)。 学习不打烊,充电加油只为遇到更好的本人,365天无节假日,每天早上5点纯手工公布面试题(死磕本人,愉悦大家)。心愿大家在这虚夸的前端圈里,放弃沉着,保持每天花20分钟来学习与思考。在这变幻无穷,类库层出不穷的前端,倡议大家不要等到找工作时,才狂刷题,提倡每日学习!(不忘初心,html、css、javascript才是基石!)欢送大家到Issues交换,激励PR,感激Star,大家有啥好的倡议能够加我微信一起交换探讨!心愿大家每日去学习与思考,这才达到来这里的目标!!!(不要为了谁而来,要为本人而来!)交换探讨欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个[Star]

January 1, 2021 · 1 min · jiezi

关于布局:前端面试每日-31-第530天

明天的知识点 (2020.09.27) —— 第530天 (我也要出题)[html] 写一个布局,满足当页面滚动时,左侧固定不动,右侧的最小高度与左侧一样[css] 伪类选择器和伪元素选择器有什么区别?[js] 接口申请时须要做哪些平安解决?怎么做?[软技能] 你之前公司有做前端的根底建设吗?都有哪些方面?《论语》,曾子曰:“吾日三省吾身”(我每天屡次检查本人)。前端面试每日3+1题,以面试题来驱动学习,每天提高一点!让致力成为一种习惯,让奋斗成为一种享受!置信 保持 的力量!!!欢送在 Issues 和敌人们一起探讨学习! 我的项目地址:前端面试每日3+1【举荐】欢送跟 jsliang 一起折腾前端,零碎整顿前端常识,目前正在折腾 LeetCode,打算买通算法与数据结构的任督二脉。GitHub 地址 微信公众号欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个Star, 同时欢送微信扫码关注 前端剑解 公众号,并退出 “前端学习每日3+1” 微信群互相交换(点击公众号的菜单:交换)。 学习不打烊,充电加油只为遇到更好的本人,365天无节假日,每天早上5点纯手工公布面试题(死磕本人,愉悦大家)。心愿大家在这虚夸的前端圈里,放弃沉着,保持每天花20分钟来学习与思考。在这变幻无穷,类库层出不穷的前端,倡议大家不要等到找工作时,才狂刷题,提倡每日学习!(不忘初心,html、css、javascript才是基石!)欢送大家到Issues交换,激励PR,感激Star,大家有啥好的倡议能够加我微信一起交换探讨!心愿大家每日去学习与思考,这才达到来这里的目标!!!(不要为了谁而来,要为本人而来!)交换探讨欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个[Star]

September 27, 2020 · 1 min · jiezi

关于布局:前端面试每日-31-第486天

明天的知识点 (2020.08.14) —— 第486天 (我也要出题)[html] 写一个三栏布局,两边固定,两头自适应[css] 如何优化打印款式?[js] 在多个页面之间须要传递参数,你是如何传递这些参数的?[软技能] URL和URI有什么区别?《论语》,曾子曰:“吾日三省吾身”(我每天屡次检查本人)。前端面试每日3+1题,以面试题来驱动学习,每天提高一点!让致力成为一种习惯,让奋斗成为一种享受!置信 保持 的力量!!!欢送在 Issues 和敌人们一起探讨学习! 我的项目地址:前端面试每日3+1【举荐】欢送跟 jsliang 一起折腾前端,零碎整顿前端常识,目前正在折腾 LeetCode,打算买通算法与数据结构的任督二脉。GitHub 地址 微信公众号欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个Star, 同时欢送微信扫码关注 前端剑解 公众号,并退出 “前端学习每日3+1” 微信群互相交换(点击公众号的菜单:交换)。 学习不打烊,充电加油只为遇到更好的本人,365天无节假日,每天早上5点纯手工公布面试题(死磕本人,愉悦大家)。心愿大家在这虚夸的前端圈里,放弃沉着,保持每天花20分钟来学习与思考。在这变幻无穷,类库层出不穷的前端,倡议大家不要等到找工作时,才狂刷题,提倡每日学习!(不忘初心,html、css、javascript才是基石!)欢送大家到Issues交换,激励PR,感激Star,大家有啥好的倡议能够加我微信一起交换探讨!心愿大家每日去学习与思考,这才达到来这里的目标!!!(不要为了谁而来,要为本人而来!)交换探讨欢送大家前来探讨,如果感觉对你的学习有肯定的帮忙,欢送点个[Star]

August 14, 2020 · 1 min · jiezi

CSS-常用布局在小程序中的应用

CSS 常用布局在小程序中的应用所有css布局的根本都是3个基本概念:定位、浮动、外边距操纵我们其他的布局实现方式,都是基于正常的文档流来进行的。所以我们先来看看什么是正常的文档流。 正常文档流(Normal Flow):正常布局流是指在不对页面进行任何布局控制时,浏览器默认的HTML布局方式。默认的文档流,确保在没有任何css样式的时候,还能够正确的渲染和显示内容。使页面具有比较好的可读性以及合理性。(position display float table flex-box grid-layout)默认情况下,元素是如何布局的? (引用自 https://developer.mozilla.org)首先,取得元素的内容来放在一个独立的元素盒子中,然后在其周边加上内边距、边框和外边距。(盒子模型)一个块级元素的内容宽度默认是其父元素的100%,其高度与其内容高度一致。行内元素的height width与内容一致。你无法设置行内元素的height width。 如果你想控制行内元素的尺寸,你需要为元素设置display: block; (或者,display: inline-block; inline-block 混合了inline 和 block的特性。)那独立元素之间又是如何相互影响的呢? 正常布局流是一套在浏览器视口内放置、组织元素的系统。默认情况下,块级元素按照在文档中书写出现的顺序放置 --- 每个块级元素会在上一个元素下面另起一行,它们会被设置好的margin 分隔。行内元素的表现有所不同 --- 它们不会另起一行;只要在其父级块级元素的宽度内有足够的空间,它们与其他行内元素、相邻的文本内容(或者被包裹的)被安排在同一行。如果空间不够,溢出的文本或元素将移到新的一行。如果两个相邻的元素都设置了margin 并且两个margin有重叠,那么更大的设置会被保留,小的则会消失 --- 这被称为外边距叠加。我们在传统的css布局当中,不管是css2还是css3,大致囊括了静态布局、流式布局、弹性布局、自适应布局、响应式布局、网格布局。对于css的这些布局方式,在小程序中又是怎样的呈现方式呢?我们分别来看一看。 1. 静态布局 (Static Layout)固定宽度布局。宽度以像素为单位。(当然我们还可以使用其他的绝对长度单位)缺点:无论窗口多大,尺寸不变。无法充分利用空间。行长和文本易读性不好。静态布局就是传统的网站形式:对于PC设计一个居中布局,窗口缩小时,内容被遮挡,呈现横竖向滚动条。对于移动设备,单独建立一个m.域名及相应的移动网站。静态布局在小程序中一般很少出现。因为如果使用静态布局,就无法做到不同屏幕自适应,750px的设计稿在小屏幕的手机上就会出现滚动条。 呈现效果如下: // wxml 代码<view class='page'> <view class='header'>静态布局</view> <view class='content'> <text>静态布局内容部分在小程序中的呈现。小程序page样式在overflow-x上默认是hidden。</text> </view></view>//样式如下page { overflow-x:scroll;}.page, .header, .content { width: 750px;}.content { background: #ccc;}2. 流式布局 (Liquid Layout)(百分比布局)流式布局是页面元素的宽度按照屏幕分辨率进行适配调整,但整体布局不变。使用百分比实现:流式布局能够相对于浏览器窗口进行伸缩。缺点:窗口宽度较小,行变得非常窄,很难阅读。需要设置min-width来解决,但是如果min-width较大的话,也会有静态布局相同的限制。宽度太宽,同样会存在行长过长的问题。流式布局(Liquid)的特点(也叫"Fluid") 是页面元素的宽度按照屏幕进行适配调整,主要的问题是如果屏幕尺度跨度太大,那么在相对其原始设计而言过小或过大的屏幕上不能正常显示。我们来看看在小程序中使用流式布局的效果: 在不同的屏幕大小下呈现出来的效果如下: <!-- wxml代码 --><view class='page fluid'> <view class='header fluid__header'>流式布局</view> <view class='content'> <!-- 两列 --> <view class='wrapper'> <view class='grid-item grid-half'> <text>我是左侧部分</text> </view> <view class='grid-item grid-half'> <text>我是右侧部分</text> </view> </view> <!-- 三列 --> <view class='wrapper'> <view class='grid-item grid-three'> <text>我是左侧部分</text> </view> <view class='grid-item grid-three'> <text>我是中间部分</text> </view> <view class='grid-item grid-three'> <text>我是右侧部分</text> </view> </view> </view></view>/* css代码 */.fluid, .content, .wrapper { width: 100%; box-sizing: border-box;}.wrapper { height: 200px; margin-bottom: 20px;}.wrapper .grid-item { height: 100%; background: #ccc; display: inline-block; box-sizing: border-box;}.wrapper .grid-half { width: 48%;}.wrapper .grid-half:nth-child(2n) { margin-left: 4%;}.wrapper .grid-three { width: 32%;}.wrapper .grid-three:not(:first-child) { margin-left: 2%;}3. 弹性布局 (Flex Layout)CSS 弹性盒子布局是 CSS 的模块之一,定义了一种针对用户界面设计而优化的 CSS 盒子模型。在弹性布局模型中,弹性容器的子元素可以在任何方向上排布,也可以“弹性伸缩”其尺寸,既可以增加尺寸以填满未使用的空间,也可以收缩尺寸以避免父元素溢出。子元素的水平对齐和垂直对齐都能很方便的进行操控。通过嵌套这些框(水平框在垂直框内,或垂直框在水平框内)可以在两个维度上构建布局。对Flex的理解可以参考阮一峰 Flex 布局教程:语法篇。在此不做详细说明。那我们看看在小程序中Flex布局的使用。依然还是上一个例子中的wxml,我们只需要修改对应的class以及css即可。 ...

August 7, 2019 · 2 min · jiezi

CSS-常用布局在小程序中的应用

CSS 常用布局在小程序中的应用所有css布局的根本都是3个基本概念:定位、浮动、外边距操纵我们其他的布局实现方式,都是基于正常的文档流来进行的。所以我们先来看看什么是正常的文档流。 正常文档流(Normal Flow):正常布局流是指在不对页面进行任何布局控制时,浏览器默认的HTML布局方式。默认的文档流,确保在没有任何css样式的时候,还能够正确的渲染和显示内容。使页面具有比较好的可读性以及合理性。(position display float table flex-box grid-layout)默认情况下,元素是如何布局的? (引用自 https://developer.mozilla.org)首先,取得元素的内容来放在一个独立的元素盒子中,然后在其周边加上内边距、边框和外边距。(盒子模型)一个块级元素的内容宽度默认是其父元素的100%,其高度与其内容高度一致。行内元素的height width与内容一致。你无法设置行内元素的height width。 如果你想控制行内元素的尺寸,你需要为元素设置display: block; (或者,display: inline-block; inline-block 混合了inline 和 block的特性。)那独立元素之间又是如何相互影响的呢? 正常布局流是一套在浏览器视口内放置、组织元素的系统。默认情况下,块级元素按照在文档中书写出现的顺序放置 --- 每个块级元素会在上一个元素下面另起一行,它们会被设置好的margin 分隔。行内元素的表现有所不同 --- 它们不会另起一行;只要在其父级块级元素的宽度内有足够的空间,它们与其他行内元素、相邻的文本内容(或者被包裹的)被安排在同一行。如果空间不够,溢出的文本或元素将移到新的一行。如果两个相邻的元素都设置了margin 并且两个margin有重叠,那么更大的设置会被保留,小的则会消失 --- 这被称为外边距叠加。我们在传统的css布局当中,不管是css2还是css3,大致囊括了静态布局、流式布局、弹性布局、自适应布局、响应式布局、网格布局。对于css的这些布局方式,在小程序中又是怎样的呈现方式呢?我们分别来看一看。 1. 静态布局 (Static Layout)固定宽度布局。宽度以像素为单位。(当然我们还可以使用其他的绝对长度单位)缺点:无论窗口多大,尺寸不变。无法充分利用空间。行长和文本易读性不好。静态布局就是传统的网站形式:对于PC设计一个居中布局,窗口缩小时,内容被遮挡,呈现横竖向滚动条。对于移动设备,单独建立一个m.域名及相应的移动网站。静态布局在小程序中一般很少出现。因为如果使用静态布局,就无法做到不同屏幕自适应,750px的设计稿在小屏幕的手机上就会出现滚动条。 呈现效果如下: // wxml 代码<view class='page'> <view class='header'>静态布局</view> <view class='content'> <text>静态布局内容部分在小程序中的呈现。小程序page样式在overflow-x上默认是hidden。</text> </view></view>//样式如下page { overflow-x:scroll;}.page, .header, .content { width: 750px;}.content { background: #ccc;}2. 流式布局 (Liquid Layout)(百分比布局)流式布局是页面元素的宽度按照屏幕分辨率进行适配调整,但整体布局不变。使用百分比实现:流式布局能够相对于浏览器窗口进行伸缩。缺点:窗口宽度较小,行变得非常窄,很难阅读。需要设置min-width来解决,但是如果min-width较大的话,也会有静态布局相同的限制。宽度太宽,同样会存在行长过长的问题。流式布局(Liquid)的特点(也叫"Fluid") 是页面元素的宽度按照屏幕进行适配调整,主要的问题是如果屏幕尺度跨度太大,那么在相对其原始设计而言过小或过大的屏幕上不能正常显示。我们来看看在小程序中使用流式布局的效果: 在不同的屏幕大小下呈现出来的效果如下: <!-- wxml代码 --><view class='page fluid'> <view class='header fluid__header'>流式布局</view> <view class='content'> <!-- 两列 --> <view class='wrapper'> <view class='grid-item grid-half'> <text>我是左侧部分</text> </view> <view class='grid-item grid-half'> <text>我是右侧部分</text> </view> </view> <!-- 三列 --> <view class='wrapper'> <view class='grid-item grid-three'> <text>我是左侧部分</text> </view> <view class='grid-item grid-three'> <text>我是中间部分</text> </view> <view class='grid-item grid-three'> <text>我是右侧部分</text> </view> </view> </view></view>/* css代码 */.fluid, .content, .wrapper { width: 100%; box-sizing: border-box;}.wrapper { height: 200px; margin-bottom: 20px;}.wrapper .grid-item { height: 100%; background: #ccc; display: inline-block; box-sizing: border-box;}.wrapper .grid-half { width: 48%;}.wrapper .grid-half:nth-child(2n) { margin-left: 4%;}.wrapper .grid-three { width: 32%;}.wrapper .grid-three:not(:first-child) { margin-left: 2%;}3. 弹性布局 (Flex Layout)CSS 弹性盒子布局是 CSS 的模块之一,定义了一种针对用户界面设计而优化的 CSS 盒子模型。在弹性布局模型中,弹性容器的子元素可以在任何方向上排布,也可以“弹性伸缩”其尺寸,既可以增加尺寸以填满未使用的空间,也可以收缩尺寸以避免父元素溢出。子元素的水平对齐和垂直对齐都能很方便的进行操控。通过嵌套这些框(水平框在垂直框内,或垂直框在水平框内)可以在两个维度上构建布局。对Flex的理解可以参考阮一峰 Flex 布局教程:语法篇。在此不做详细说明。那我们看看在小程序中Flex布局的使用。依然还是上一个例子中的wxml,我们只需要修改对应的class以及css即可。 ...

July 14, 2019 · 2 min · jiezi

开源背后-面对端侧推理引擎的挑战阿里工程师如何应对

阿里妹导读:MNN(Mobile Neural Network)已于今年5月7日在 Github 上正式开源。淘宝无线开发专家——陈以鎏(离青)在 GMTC 全球大前端技术大会为大家分享了 MNN 开发、开源中的思考与总结,通过淘宝在移动 AI 上的实践经验,你将会了解移动 AI 的发展状况和应用场景,以及通过端侧推理引擎了解移动/ IoT 深度优化策略。开源与背景 人工智能从 2006 年开始,迎来了第三次浪潮。随着 AlphaGo 在 2016 年、 2017 年先后战胜李世石和柯洁,人工智能彻底进入了公众的视野。人工智能大热的背后,是大数据的积累,是深度学习的发展,也是设备算力的提升。与此同时,深度学习的框架也在不断演进 —— 从 Torch、Caffe 到 TensorFlow、PyTorch ,再到更面向移动端的 CoreML、NNAPI、NCNN、MACE 等。淘宝的深度学习推理引擎 MNN 也于 2019 年 5 月宣布开源。 MNN 是一个轻量级的深度学习端侧推理引擎,核心解决深度神经网络模型在端侧推理运行问题,涵盖深度神经网络模型的优化、转换和推理。目前,MNN已经在手淘、手猫、优酷、聚划算、UC、飞猪、千牛等 20 多个 App 中使用,覆盖直播、短视频、搜索推荐、商品图像搜索、互动营销、权益发放、安全风控等场景,每天稳定运行上亿次。此外,菜鸟自提柜等 IoT 设备中也有应用。在 2018 年双十一购物节中,MNN 在天猫晚会笑脸红包、扫一扫、明星猜拳大战等场景中使用。 开源地址:https://developer.aliyun.com/... MNN 项目从 2017 年开始启动,在经历一年多的开发迭代并通过了淘宝双十一的考验后,于 2018 年底启动开源计划,在历时小半年的开源改造后,今年 5 月份正式在 Github 开源。 开源首先还是因为经历过双十一之后,我们觉得自己做好了准备,开源有助于我们鞭策自己,把 MNN 做的更好;另一方面,业界的开源方案,不论是 TensorFlow Lite 、 NCNN 还是 Mace ,都给了我们很好的输入和借鉴,我们也希望借着开源,将我们的思考和创新回馈社区。 ...

July 3, 2019 · 3 min · jiezi

CSS三栏布局多种方法详解比较兼容性

<!-- 题目:假设高度已知,请写出三栏布局,其中左栏、右栏宽度各为300px,中间自适应 -->这是一道经典的面试题,下面记录了css布局的5种方法。 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>三栏布局</title> <style> * { margin: 0; padding: 0; } </style></head><body><!--5种解决方案--></body>1. 浮动解决方案: <!-- 1. 浮动解决方案 --> <scetion class="layout float"> <!-- 样式 --> <style media="screen"> .layout.float article div { min-height: 80px; } .layout.float .left { width: 300px; background-color: red; float: left; } .layout.float .center { background-color: yellow; } .layout.float .right { width: 300px; background-color: blue; float: right; } </style> <!-- 结构 --> <!-- 注意:结构中 右浮动的div一定要写在 center 的前面,否则无效. --> <h2>三栏布局</h2> <article class="left-ritgh-center"> <div class="left"></div> <div class="right"></div> <div class="center"> <h2>浮动解决方案</h2> 1.这是三栏布局的浮动解决方案; 2.这是三栏布局的浮动解决方案; 3.这是三栏布局的浮动解决方案; 4.这是三栏布局的浮动解决方案; 5.这是三栏布局的浮动解决方案; 6.这是三栏布局的浮动解决方案; </div> </article> </scetion>2. 绝对定位解决方案: <!-- 2. 绝对定位解决方案 --> <section class="layout absolute"> <!-- 样式 --> <style> .layout.absolute article div { min-height: 80px; position: absolute; } .layout.absolute .left { width: 300px; background-color: red; left: 0; } .layout.absolute .center { background-color: yellow; left: 300px; right: 300px; } .layout.absolute .right { width: 300px; background-color: blue; right: 0; } </style> <!-- 结构 --> <h1>三栏布局</h1> <article class="left-center-right"> <div class="left"></div> <div class="center"> <h2>绝对定位解决方案</h2> 1.这是三栏布局的绝对定位解决方案; 2.这是三栏布局的绝对定位解决方案; 3.这是三栏布局的绝对定位解决方案; 4.这是三栏布局的绝对定位解决方案; 5.这是三栏布局的绝对定位解决方案; 6.这是三栏布局的绝对定位解决方案; </div> <div class="right"></div> </article> </section>3. flexbox 解决方案: <!-- 3. flexbox解决方案 --> <section class="layout flexbox"> <!-- 样式 --> <style> /* flexbox解决方案在浏览器中显示时被上面的绝对定位解决方案挡住了,设置一个margin-top */ .layout.flexbox { margin-top: 110px; } /* 设置最低高度 */ .layout.flexbox article div { min-height: 80px; } .layout.flexbox article { display: flex; } .layout.flexbox .left { width: 300px; background-color: red; } .layout.flexbox .center { /*center部分增长 使整行填充*/ flex: 1; background-color: yellow; } .layout.flexbox .right { width: 300px; background-color: blue; } </style> <!-- 结构 --> <h1>三栏布局</h1> <article class="left-center-right"> <div class="left"></div> <div class="center"> <h2>flexbox解决方案</h2> 1.这是三栏布局的flexbox解决方案; 2.这是三栏布局的flexbox解决方案; 3.这是三栏布局的flexbox解决方案; 4.这是三栏布局的flexbox解决方案; 5.这是三栏布局的flexbox解决方案; 6.这是三栏布局的flexbox解决方案; </div> <div class="right"></div> </article> </section>4. 表格布局解决方案 <!-- 4. 表格布局解决方案 --> <section class="layout table"> <!-- 样式 --> <style> .layout.table .left-center-right { width: 100%; min-height: 80px; display: table; } .layout.table .left-center-right>div { display: table-cell; } .layout.table .left { width: 300px; background-color: red; } .layout.table .center { background-color: yellow; } .layout.table .right { width: 300px; background-color: blue; } </style> <!-- 结构 --> <h1>三栏布局</h1> <article class="left-center-right"> <div class="left"></div> <div class="center"> <h2>表格布局解决方案</h2> 1.这是三栏布局的表格布局解决方案; 2.这是三栏布局的表格布局解决方案; 3.这是三栏布局的表格布局解决方案; 4.这是三栏布局的表格布局解决方案; 5.这是三栏布局的表格布局解决方案; 6.这是三栏布局的表格布局解决方案; </div> <div class="right"></div> </article> </section>5. 网格布局解决方案: <!-- 5. 网格布局解决方案 --> <section class="layout grid"> <!-- 样式 --> <style> .layout.grid .left-center-right { width: 100%; display: grid; /* 网格行高 */ grid-template-rows: 100px; /* 网格列宽 */ grid-template-columns: 300px auto 300px; } .layout.grid .left { background-color: red; } .layout.grid .center { background-color: yellow; } .layout.grid .right { background-color: blue; } </style> <!-- 结构 --> <h1>三栏布局</h1> <article class="left-center-right"> <div class="left"></div> <div class="center"> <h2>网格布局解决方案</h2> 1.这是三栏布局的网格布局解决方案; 2.这是三栏布局的网格布局解决方案; 3.这是三栏布局的网格布局解决方案; 4.这是三栏布局的网格布局解决方案; 5.这是三栏布局的网格布局解决方案; 6.这是三栏布局的网格布局解决方案; </div> <div class="right"></div> </article> </section>效果: ...

May 23, 2019 · 2 min · jiezi

CSS布局基础三栏布局

前言大家总是听到双飞翼布局和圣杯布局...也不知道是谁取的名字,我就叫三栏布局吧。虽然他们有些细微的区别,但本质上都是实现一个三栏布局,即左右两栏固定,中间自适应。 实现方案网上随便一搜,全是实现方案,少到两三种,多到七八种。各种方法都有优缺点,但我觉得比较实用的方法主要就那三四种,因为很多方法其实是被淘汰的或者说太麻烦(如表格布局)。 浮动方案绝对定位方案flex布局方案网格布局(本章不讲)方案一:浮动方案实现思路:先将左右两栏进行浮动,左边栏向左浮动,右边栏向右浮动就能固定在两边。但是要注意一点,这种方法要将中间栏放在最后,因为如果将中间栏放在中间,并且没有对自身进行浮动的话,会占据文档中的位置,导致右边栏并不能完全和左边栏平齐。 HTML: <!-- 三栏布局 浮动定位--> <div class="layout"> <header>头部</header> <main> <div class="left">左边栏</div> <div class="right">右边栏</div> <div class="center">中间栏</div> </main> <footer>底部</footer> </div> <!-- 三栏布局 浮动定位-->SCSS: //浮动布局.layout { color:white; text-align: center; height: 100%; overflow: hidden; header,footer{ width: 100%; height: 70px; background: rgb(202,132,2); } footer { position: absolute; bottom: 0; } main { width: 100%; height: 100%; background: red; .left,.right { width: 300px; height: 100%; background: rgb(14, 214, 171); } .left { float:left; } .right{ float:right; } .center { height:100%; background: rgb(26, 26, 122); } } }效果: ...

May 5, 2019 · 2 min · jiezi

Gartner阿里云亚太市场份额第一超过亚马逊和微软总和

4月24日,据彭博社报道,阿里云在亚太云计算市场份额达19.6%,超过亚马逊和微软的总和。这是阿里云连续第二年位居亚太市场第一,份额同比上年增长4.7个百分点,持续扩大领先优势。 报道引述研究机构Gartner的最新市场调研数据,在云计算基础设施领域,2018年阿里云在亚太区域市场份额为19.6%,同期亚马逊为11%、微软为8%。同比2017年,阿里云市场份额增长了4.7个百分点。同时,在全球范围内,阿里云持续保持全球前三的领先地位。 彭博表示,阿里云在过去三年里持续保持高速增长,远超行业平均增长水平。在国际市场上,阿里云的增长更加迅速,其在亚太区域拥有最为广泛的云计算基础设施布局,并且是唯一在马来西亚和印度尼西亚开设数据中心的云服务商。 公开资料显示,2018自然年阿里云营收达到213.4亿,四年间增长20倍。高速增长的同时,阿里巴巴还在不断加码对云业务的战略投入,将全集团的技术与云全面结合并对外输出,目标是构建数字经济时代的云智能基础设施。 同时,阿里巴巴自身也将在未来1到2年内All in Cloud,为数字经济发展提供基于云上最佳实践的新技术和新理念。 本文作者:阿里云头条阅读原文 本文为云栖社区原创内容,未经允许不得转载。

April 25, 2019 · 1 min · jiezi

UI2Code智能生成Flutter代码——机器生成代码

背景在《UI2CODE–整体设计》篇中,我们提到UI2Code工程的整体流程。前步图片分析之后,我们可以得到对应的DSL布局描述。利用DSL的资讯,结合IntelliJ Plugin介面工具,面向使用者提供生成对应Flutter代码。本篇主要介绍我们如何处理DSL的资讯,想法上即是Flutter的翻译机。总体概念如下:输入的DSL是什么?DSL做为一种描述语言,抽象表示为了解决某一类任务而专门设计的计算机语言。在此我们的DSL代表图像识别和布局识别侧的输出,为一JSON格式。这些资讯主要描述了这个图层(Layer)的范围(Frame)、是什么样子的类型(Type)、是什么样子的样式(Styles)、含有哪些数据(Value)等等。图层集(Layers)栏位则代表了这张视觉稿的所有图层。核心思路本节的目标是将DSL翻译成目标的Flutter代码。我们首先需要理解的是分散的图层间的关系,可能会有交叠、可能是并列排版。知道了关系之后,需想办法转化成Flutter widget的视图,根据此视图来生产对应代码。架构上我们把DSL tree和Flutter tree的建立,分拆为两个独立的分界。这样比较容易定义问题,并且保持弹性。如果今天的目标语言换成Weex或是iOS UI,我们就只需要更动代码翻译的模组。第一把刀:DSL tree建立上图的左侧代表了来源DSL的layers资料,代表者一个一个的图层。右侧是目标的DSL Tree,这棵树的结构上明确叙述了图层之间的包裹、交叠等关系。并且包含了某些特殊关系的节点聚合。作法上利用每个Layer的Frame,以及所属的类别(文字、图像、容器),利用下面的规则组合树的关系:图层之间的包裹关系,例如某些图层为容器,代表下面是可以挂其他节点的(这边带有背景属性的容器,我们定义称之为Shape)区块式组件(Block, 如ListView/GridView)。可以将图层组成View item的关系闲鱼定义的组件资讯(如CI以及BI),这部份非闲鱼工程可以忽略重复布局(Repeat)的资讯,将相同的图层归类合并,目的为简化树根据以上我们采用了分层,由大至小的次序将Layer分群合并。另外,在合并时layer之间彼此可能有关联;它们可能同属于Block,也可能同属于某个Repeat。所以对于上面定义的Repeat、BI、Block、CI、Shape都可能有交错的嵌套关系,这是必须要处理的部份。第二把刀:Flutter tree建立在Flutter Tree的建构中,核心概念先处理布局。布局的概念如剥洋葱一般,我们先去除四周的padding,然后以人类视觉layout的直觉先尝试横切分,再进行竖切分。1.先剥洋葱去除padding2.接著我们的算法会先尝试是否可以横切,如下图我们可以切割成为Row1/ Row23.针对Row1在尝试再进行竖切,如下图可以得到Column1/ Column2/ Column3根据以上切分的规则,我们就可以定义出如Row、Column、Padding的几个节点,以及它们的Parent/ Child关系。将DSL tree同一层的节点做切分,一边切分一边建立Flutter node,遍历完整颗DSL,即可得到粗略的Flutter tree关系。= 无法切分时的处理当图层切分不开时,这时候就要使用绝对布局叠层的概念,这个概念在Flutter内称之为Stack。多个图层在DSL tree的关系为兄弟节点,根据此些图层的Frame,我们判断出来它们是彼此相交的,我们会以Z-order概念,来决定上下交叠的关系。最后,这些图层将组成一个新Stack节点,并且产生此节点的Frames为此些图层覆盖的范围。= 针对文字的进阶处理基本上交叠的图层以Stack的处理就可以正确显示,但在文字图层上可能含有误区。如上图因为文字本身的上下左右是含有padding的,在我们图层的识别时,可能会计算出彼此的frame是交叠的,但实际上UI希望它们并不是Stack关系。为了解决这个问题,我们引入了一个oriFrame的概念,用文字最原始的像素当做是oriFrame。所以遇到为文字的图层时,我们会先判断本身的oriFrame是否交叠,如果是的话才采用Stack切割,否则就以此oriFrame对原始的frame做修正。文字还有什么特性?另外,因为文字的内容通常是动态的,所以拥有了”所见不一定为所得”的特性。这些特性主要包含了是否该换行、内容区域是否可以拉伸、文字Padding等,这些特性都会影响到我们的布局。以下图为例,我们在处理Layout时肉眼很明显可以知道这些特徵。文字的行数我们可以以视觉稿当做最大显示范本,文字区域的宽度部分,则需要特别判断哪些区域是可以被拉伸的。确立文字范围在决定拉伸对象之前,我们需要定义哪些widget是将内容完整显示,不能被拉伸的:如图片、Container容器、Stack区域、Component组件接著处理的流程如下:首先判断所有Child内是否有多行文字或宽度固定的文字,如果是的话针对其处理。需要加上Flex。若无以上的状况,则判断Child间的Padding关系如果可拉伸的widget的Padding大于平均值的个数有多个,则这些都加上Flex如果只有单个时,则找寻最大Padding的widget(使用分群拉伸算法)最后,但当Row里面存有拉伸的状况时,需要把Row的最后一个child加上Right padding,否则拉伸元素会填满父容器。分群拉伸算法:这个算法的目的是找到最佳拉伸的对象。我们的思考上将Widget做分组,分组后判断整体的Alignment(如左右对齐)或是拉伸关系。若在拉伸状况下,判断适合让哪个组别拉伸,在进一步判断适合让组别的内部元件拉伸。举例如下为一个Row排列的控件,其中排列为Image、CI、Text1、Text2、Text3:依据Widget之间的距离,在上图分为了Group1及Group2两个群体。先以Group1判断是否存在可拉伸的对象, 接著才判断Group2。所以这5个Widget分别得到了3, 2, 1, 4, 5的优先级。以本例而言,Text1为最高优先,而且其为可拉伸的,故决定将Flex属性加于此。在Expanded的处理上,是我们目前遇到最大的困难点,甚至人工判断都可能有歧义。上面的规则是我们归纳出众多视觉稿的通解,但不能100%完全解决问题。所以这部份判断错误的部分,我们期待在Plugin的交互中使用人工解决。= 判断Alignment优化以上的处理已经可以正确生成Flutter tree,但是我们想进一步地将Flutter代码更加优雅。在此我们针对了三种元件的Alignment做了处理,分别是Container、Row、Column,其概念都是分析内部元件的padding关系,决定为居左、居中、或是居右对齐。举例如Column内部的children我们去判断左右的padding是否相等。若是则移除其padding,并且加上crossAxisAlignment为center。针对Row/ Container我们则会判断crossAxisAlignment(垂直方向)以及mainAxisAlignment(水平方向)。水平部份,这边我们采用更精细的方法,我们利用欧式距离建立一个非监督算法,计算views是更为接近哪一个(居左、居中、居右)。算法这边先不详述,之后再以篇幅介绍。最后:生成Flutter代码经过前面的步骤后,最终我们产生了一个Flutter Tree。生成时在节点的定义上,我们分为了两种,分别是View与Layout,以是否可以拥有Child为区别。以下是我们针对Flutter Tree所定义的部份类别:在节点的定义中,皆存储了各节点的Parent、Child属性。根据这些关系,我们定义每个节点的代码样板,例如FColumn对应的样板为:new Column(#{alignment},children: <Widget>[ #{children},]), 最后我们以Root widget开始遍历整颗树,将每个节点所生成的Flutter代码结合,这样我们就可以得到整个Widget tree的代码了。数据分离为了更好的重复利用生成代码,我们把生成的代码和数据再进一步做分离。分离后输出分为代码区以及Data model数据区:我们切割这些区域的目的为简化Widget tree直观上的代码复杂度,以及将数据抽离,让资料可由外部呼叫传入,以达成动态性。整体架构回顾总合以上的概念,工程的细部架构如下:前面所说的针对文字以及Alignment的处理,在这边我们设计了一个工厂模式,如上图中经过Flutter Tree Builder后,我们可以去遍历整颗Widget tree,在工厂中判断判断符合条件的规则,经过处理去震荡优化原本的Widget tree。在这边未来我们可以不断地加上合适的规则,让Widget tree更加优化。整体架构使用静态分析的方法,读到此各位可能会有疑问:一些如动态的事件、View的Visibility、Input输入文字框等怎么处理?由于这些动态性在静态分析下无法解决,所以我们增强了Plugin上的编辑性,使用者只要勾选某些属性,即会在生成代码时自动判断,在Flutter自动增加对应的逻辑。以弥补静态图无法处理的问题。由于UI的灵活性高,十个人写的代码可能有十种不同风格。并且在分析上游的UI2DSL,以及Flutter代码的翻译,某些部份的精确性取决于我们的样本的认知,是否能够在有限的样本内观查出泛化的定律,分析上还是存有很多挑战性。结合落地业务在整个UI2CODE的效果中,大约七成以上的页面都可以正确分析出来,剩下的是一些小细节如文字的处理等,基本上我们工具都能够将大框架的处理好,使用者可能只需微小的调整。UI2CODE案子在内部团队上线后,已经在闲鱼APP内的"玩家页面"采用了自动化生成的代码。在采用自动化工具后,大约减少了三分之二的UI开发时间(因初期还在熟悉工作流程,未来相信可以更快速)。同时,若在客户端大量采用我们工具,还可以让团队的代码结构有一些的规范,让生成工具来规范Widget UI以及Data Binding的框架,一致性以及后续的维护,相信是一个很大的诱因。并且闲鱼团队近期计画开发一款新的APP,在初期时能够快速开发UI,也将采用我们的工具。期望有更多的业务和经验积累。后续计画近期我们推出了第一版UI2CODE,先计画于内部团队使用,利用使用的经验,让我们在叠代之下不断提高准确性。并且,我们正在调研结合NLP以及AST(语法树)的可能性,希望能够产出更有质量的代码。我们也期望未来能将此工具开放于Flutter community,对于推动整个Flutter技术有所推进。希望能让更多人跟我们一起找寻更有效率的写代码方法,如果有任何想法欢迎与我们交流,我们也持续不断地在进化工具中,谢谢各位的阅读!本文作者: 闲鱼技术-上叶,余晏阅读原文本文为云栖社区原创内容,未经允许不得转载。

April 4, 2019 · 1 min · jiezi

一些有趣的 CSS 魔法和布局(下)

前言上一篇 一些有趣的 CSS 魔法和布局(上) 中,我们聊了一些有趣且实用的布局。今天,将呈现一些 CSS 带来的魔法特效,有部分特效可以帮我们省去不少工作量。鼠标悬浮使内容自撑开在以前遇到这个需求的时候,我们可能会想到用 JS 来操作内容的显式与否。现在,CSS3 的 transition 可以帮我们更简单地实现。<ul> <li style=“padding-bottom: 20px;"> <div class=“head”> 列表1 </div> <div class=“body”> 列表内容<br> 内容列表内容<br> 内容列表内容<br> 内容 </div> </li> <li> <div class=“head”> 列表2 </div> <div class=“body”> 列表内容<br> 内容列表内容<br> 内容列表内容<br> 内容 </div> </li></ul>.body { max-height: 0; overflow: hidden; transition: all 1s ease-out;}li:hover .body { max-height: 300px; transition-timing-function: ease-in;}transition: all 1s ease-out; 这是一种简写,transition 有 4 个过渡属性:transition-property、transition-duration、transition-timing-function、transition-delay。transition-timing-function 规定了速度效果的速度曲线,它有以下几种约定的属性。transition-timing-function: linear|ease|ease-in|ease-out|ease-in-out|cubic-bezier(n,n,n,n);让图片变成黑白色有一些特殊的时段,比如需要降半旗,在大环境下,各大网站为了表示默哀,会把颜色鲜艳的图片变灰变暗,过了这段时间后再恢复原本的颜色。过去,会让 UI 同学临时赶工改图,然后再上传更新,过程麻烦,还会造成存储空间的浪费。现在,CSS3 的 filter (滤镜) 属性可以更快地实现该需求。UI 同学改的,其实也是源图片的滤镜。所以,可以这样实现:.header { background: no-repeat center center; background-color: #ccc; background-attachment: scroll; background-size: cover; margin-bottom: 0; background-image: url(xxx.jpg); filter: grayscale(100%);}未加 filter 的效果如下:加了 filter 的效果:filter 的属性还有很多,其中 blur 可以实现高斯模糊,类似 IOS7 的毛玻璃效果。contrast 可以调整图片的对比度。drop-shadow 会在图片的下方合成阴影效果,等等。自定义输入框光标的颜色之前遇到过一个需求,注册登录界面的整体色调偏蓝色,连输入框的边框色都偏蓝。所以,产品希望输入框的颜色和光标都变为蓝色。设置文本的颜色方便,但光标该怎么设置颜色?CSS3 的 caret-color 可以解决这个问题。input, textarea { caret-color: blue;}它不仅对于原生的输入控件有效,对设置 contenteditable 的普通 HTML 标签也适用。不过兼容性上,safari 的 PC 版从 V11.1 开始支持,wap 版从 V11.4 也开始支持了。当然 IE 目前还没有支持。禁用文本选中在以前,如果不想让别人选中你的页面里的内容,可以用 JS 来阻止鼠标事件。而现在,只需要一句 user-select:none; 的样式就可以搞定了。body { user-select: none; // 页面中的文本不能被选中}除了 IE,兼容性都不错。在 IE 6-9 上,可以通过给 body 添加 JS 代码 onselectstart=“return false;” 来解决。反过来,对于无法被选中文本的页面,如果真想复制,也是有技巧的。方法很简单:打开 chrome 的 debug 模式,在 console 下输入 document.body.innerText,回车后就能愉快地复制了,嘿嘿控制表格单元格宽度手写原生 table 的时候,直接给单元格设置宽度并没什么作用,因为单元格的宽度是根据其内容进行调整的。但是,在使用 element-ui 的 el-table 组件时,却可以给每个单元格设置宽度。这是如何做到的呢?打开 debug,看 table 的结构,会发现在 CSS 上有个属性:table-layout: fixed;。table-layout 的默认值是 auto,当设置为 fixed 时,在 单元格 td 上设置的宽度就起作用了。用法很简单:table { table-layout: fixed; width: 100%;}流光效果某个商城上曾看到过的,鼠标 hover 出现一道流光划过的效果。用到了 CSS3 的 transform、linear-gradient、transition 等特性。<div class=“img-light”> <img src=“xxx.jpg” width=“640” height=“384”></div>.img-light { position: relative; width: 640px; height: 384px;}.img-light::after { content: “”; height: 100%; width: 100px; transform: skewX(-25deg) translateZ(0); background-image: linear-gradient(90deg, hsla(0, 0%, 100%, 0), hsla(0, 0%, 100%, 0.3) 50%, hsla(0, 0%, 100%, 0)); position: absolute; left: -150%; top: 0; z-index: 2;}.img-light:hover::after { transition: left 2s ease-in-out; left: 150%;}透传事件层遇到过一种情况,点击当前层无需触发自己的事件,但可以触发下面那层的事件,我把这种情况理解为“透传”。也就是,点击自身并无反应,相当于直接点击在了下面那层上。用 CSS3 的 pointer-events 实现很简单:div { pointer-events: none;}设置为 none,在 MDN 上的解释是:元素永远不会成为鼠标事件的 target。但是,当其后代元素的 pointer-events 属性指定其他值时,鼠标事件可以指向后代元素,在这种情况下,鼠标事件将在捕获或冒泡阶段触发父元素的事件侦听器。美化表单的 radio 和 checkbox在某个博客中看到的美化 radio 和 checkbox 的写法。.radio-beauty-container { font-size: 0; $bgc: green; %common { padding: 2px; background-color: $bgc; background-clip: content-box; } .radio-name { vertical-align: middle; font-size: 16px; } .radio-beauty { width: 18px; height: 18px; box-sizing: border-box; display: inline-block; border: 1px solid $bgc; vertical-align: middle; margin: 0 15px 0 3px; border-radius: 50%; &:hover { box-shadow: 0 0 7px $bgc; @extend %common; } } input[type=“radio”]:checked+.radio-beauty { @extend %common; }}其原理是:伪类选择器 :checked,表示对应控件元素(单选框或是复选框)选中时的样式加号 + 相邻兄弟选择器,这个符号表示选择后面的兄弟节点两者配合,就可以轻松自如控制后面元素的显示或者隐藏,或是其他样式了用 label 标签控制单/复选框的选中与否,for 属性锚定对应的单选框或是复选框,然后点击这里的 label 标签元素的时候,对应的单/复选框就会选中或是取消选中。总结CSS3 有许多很赞的特性,可以呈现非常神奇且炫酷的效果。在这儿仅展示了一些我搜罗到的,觉得有意思的特效。大家如果有值得推荐的 CSS 特效,也欢迎一起探讨学习哈岗位内推莉莉丝游戏招 中高级前端工程师 啦!!!你玩过《小冰冰传奇([刀塔传奇])》么?你玩过《剑与家园》么?你想和 薛兆丰老师 成为同事么?有兴趣的同学,可以 关注下面的公众 号加我微信 详聊哈~ ...

April 2, 2019 · 2 min · jiezi

UI2Code智能生成Flutter代码--整体设计篇

摘要: UI2CODE项目是闲鱼技术团队研发的一款通过机器视觉理解+AI人工智能将UI视觉图片转化为端侧代码的工具。背景:随着移动互联网时代的到来,人类的科学技术突飞猛进。然而软件工程师们依旧需要花费大量精力在重复的还原UI视觉稿的工作。UI视觉研发拥有明显的特征:组件,位置和布局,符合机器学习处理范畴。能否通过机器视觉和深度学习等手段自动生成UI界面代码,来解放重复劳动力,成为我们关注的方向。UI2CODE简单介绍:UI2CODE项目是闲鱼技术团队研发的一款通过机器视觉理解+AI人工智能将UI视觉图片转化为端侧代码的工具。2018年3月UI2CODE开始启动技术可行性预研工作,到目前为止,经历了3次整体方案的重构(或者重写)。我们参考了大量的利用机器学习生成代码的方案,但都无法达到商用指标,UI2CODE的主要思想是将UI研发特征分而治之,避免鸡蛋放在一个篮子里。我们着重关注以下3个问题的解法:视觉稿还原精度:我们的设计师甚至无法容忍1像素的位置偏差;准确率:机器学习还处于概率学范畴,但我们需要100%的准确率;易维护性:工程师们看的懂,改的动是起点,合理的布局结构才能保障界面流畅运行。UI2CODE运行效果:UI2CODE插件化运行效果,如下视频:进过几轮重构,最终我们定义UI2CODE主要解决feeds流的卡片自动生成,当然它也可以对页面级自动生成。视频地址:https://yunqivedio.alicdn.com…架构设计:简化分析下UI2CODE的流程:大体分为4个步骤:1.通过机器视觉技术,从视觉稿提取GUI元素2.通过深度学习技术,识别GUI元素类型3.通过递归神经网络技术,生成DSL4.通过语法树模板匹配,生成flutter代码版面分析版面分析只做一件事:切图。图片切割效果直接决定UI2CODE输出结果的准确率。我们拿白色背景的简单UI来举例:上图是一个白色背景的UI,我们将图片读入内存,进行二值化处理:def image_to_matrix(filename): im = Image.open(filename) width, height = im.size im = im.convert(“L”) matrix = np.asarray(im) return matrix, width, height得到一个二维矩阵:将白色背景的值转化为0.像切西瓜一样,我们只需要5刀,就可以将GUI元素分离,切隔方法多种多样:(下面是横切的代码片段,实际切割逻辑稍微复杂些,基本是递归过程)def cut_by_col(cut_num, _im_mask): zero_start = None zero_end = None end_range = len(_im_mask) for x in range(0, end_range): im = _im_mask[x] if len(np.where(im==0)[0]) == len(im): if zero_start == None: zero_start = x elif zero_start != None and zero_end == None: zero_end = x if zero_start != None and zero_end != None: start = zero_start if start > 0: cut_num.append(start) zero_start = None zero_end = None if x == end_range-1 and zero_start != None and zero_end == None and zero_start > 0: zero_end = x start = zero_start if start > 0: cut_num.append(start) zero_start = None zero_end = None客户端的UI基本都是纵向流式布局,我们可以先横切在纵切。将切割点的x,y轴坐标记录下来,它将是处理组件位置关系的核心。切割完成后,我们获取到2组数据:6个GUI元素图片和对应的坐标系记录。后续步骤通过分类神经网络进行组件识别。在实际生产过程中,版面分析会复杂些,主要是在处理复杂背景方面。关注我们的技术公众号,我们后续会详细分解。组件识别:进行组件识别前我们需要收集一些组件样本进行训练,使用Tensorflow提供的CNN模型和SSD模型等进行增量训练。UI2CODE对GUI进行了几十种类型分类:IMAGE, TEXT,SHAP/BUTTON,ICON,PRICE等等,分别归类为UI组件,CI组件和BI组件。UI组件,主要针对flutter原生的组件进行分类。CI组件,主要针对闲鱼自定义UIKIT进行分类。BI组件,主要针对具备一定业务意义的feed卡片进行分类。组件的识别需要反复的通过全局特征反馈来纠正,通常会采用SSD+CNN协同工作,比如下图的红色“全新“shape,这该图例中是richtext的部分,同样的shape样式可能属于button或者icon。属性提取:这块的技术点比较杂,归纳起来需要处理3部分内容:shape轮廓, 字体属性和组件的宽高。完成属性提取,基本上我们完成所有GUI信息的提取。生成的GUI DSL如下图:通过这些数据我们就可以进行布局分析了。其中文字属性的提取最为复杂,后续我们会专门介绍。布局分析:前期我们采用4层LSTM网络进行训练学习,由于样本量比较小,我们改为规则实现。规则实现也比较简单,我们在第一步切图时5刀切割的顺序就是row和col。缺点是布局比较死板,需要结合RNN进行前置反馈。视频地址:https://yunqivedio.alicdn.com…视频中展示的是通过4层LSTM预测布局结构的效果,UI的布局结构就像房屋的框架,建造完成后通过GUI的属性进行精装修就完成了一个UI图层的代码还原工作。代码生成及插件化:机器学习本质上来说还属于概率学范畴,自动生成的代码需要非常高的还原度和100%的准确率,概率注定UI2CODE很难达到100%的准确率,所以我们需要提供一个可编辑工具,由开发者通过工具能够快速理解UI的布局结构和修改布局结构。我们将UI2CODE生成的DSL TREE进行代码模板化匹配,代码模板的内容由资深的flutter技术专家来定义,它代表目前我们发现的最优代码实现方案。代码模板中会引入一些标签,由Intellij plugin来检索flutter工程中是否存在对应的自定义UIKIT,并进行替换,提高代码的复用度。整个插件化工程需要提供自定义UIKIT的检索,替换和校验工作,以及DSL Tree的创建,修改,图示等工作,总体来说,更像ERP系统,花费一些时间能够做的更加完美。小结:本篇我们简单介绍了UI2CODE的设计思路,我们将整个工程结构分为5个部分,其中4块内容核心处理机器视觉的问题,通过机器学习将它们链接起来。代码的线上发布是非常严格的事情,而单纯的机器学习方式,很难达到我们要求,所以我们选择以机器视觉理解为主,机器学习为辅的方式,构建整个UI2CODE工程体系。我们将持续关注AI技术,来打造一个完美的UI2CODE工具。本文作者:闲鱼技术-上叶阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 27, 2019 · 1 min · jiezi

css多种方式实现等高布局

本文讲的等高布局是在不手动设置元素高度的情况下,使用纯css实现各个元素高度都相当的效果。如图:1、使用table-cell实现(兼容IE8)<style> body,div,ul,li{margin: 0;padding: 0;} li{list-style: none;} .table-layout-container{ width: 50%; margin: 20px auto; } .table-row-layout{ /* 当元素display设置为table-row后,再设置宽度就没有效果了,因此需要再包裹一层div,然后给它设置宽度 */ display: table-row; } .table-cell-layout{ display: table-cell; width: 33.33%; padding: 10px; border: 1px solid #ccc; border-left: none; } .table-cell-layout:first-child{ border-left: 1px solid #ccc; }</style><body> <div class=“table-layout-container”> <ul class=“table-row-layout”> <li class=“table-cell-layout”> 行业的发展必然带来职业的细分,在前端领域也一样,目前行业前端有下面这几个方向:界面展现用户体验和可访问性方向、 后的js/nodejs开发方向、audio/video音视频富媒体方向、SVG/canvas/webGL动效创意表现与数据可视化方向、工具建设 文档管理内部站建设的前端运维方向以及会议预定团建组织对外品牌宣传的前端运营方向。 </li> <li class=“table-cell-layout”> 由于每个人的性格特质,成长经历的差异,自然适合的方向也不一样。感性细腻有设计背景可以专注于用户体验,纯逻辑工程 化思维浓郁则更适合偏后开发,数学物理强悍可以考虑数据可视化方向,沟通协调能力很强可以朝着前端运营方向努力。 </li> <li class=“table-cell-layout”> 在学生时代,大家都很纯粹,我喜欢这个就学这个,例如,很多女生很喜欢CSS,写页面,做效果。但是,等她开始找工作, 还是接触真实的行业圈子的时候,事情就发生了变化。 </li> </ul> </div></body>2、使用flex布局实现<style> body,div,ul,li{margin: 0;padding: 0;} li{list-style: none;} .flex-layout{ display: flex; width: 50%; margin: 20px auto; } .flex-item{ width: 33.33%; padding: 10px; border: 1px solid #ccc; border-left: none; } .flex-item:first-child{ border-left: 1px solid #ccc; }</style><body> <ul class=“flex-layout”> <li class=“flex-item”> 行业的发展必然带来职业的细分,在前端领域也一样,目前行业前端有下面这几个方向:界面展现用户体验和可访问性方向、偏后的js/nodejs开发方向、audio/video音视频富媒体方向、SVG/canvas/webGL动效创意表现与数据可视化方向、工具建设文档管理内部站建设的前端运维方向以及会议预定团建组织对外品牌宣传的前端运营方向。 </li> <li class=“flex-item”> 由于每个人的性格特质,成长经历的差异,自然适合的方向也不一样。感性细腻有设计背景可以专注于用户体验,纯逻辑工程化思维浓郁则更适合偏后开发,数学物理强悍可以考虑数据可视化方向,沟通协调能力很强可以朝着前端运营方向努力。 </li> <li class=“flex-item”> 在学生时代,大家都很纯粹,我喜欢这个就学这个,例如,很多女生很喜欢CSS,写页面,做效果。但是,等她开始找工作,还是接触真实的行业圈子的时候,事情就发生了变化。 </li> </ul></body>3、利用margin实现等高布局(实际开发中不建议使用)实现上面的等高布局除了可以使用table-cell、flex布局外还可以利用margin负值来实现<style> body,div,ul,li{margin: 0;padding: 0;} li{list-style: none;} .marign-layout{ width: 50%; margin: 20px auto; overflow: hidden; } .item{ float: left; width: 30%; padding: 10px; margin-bottom: -9999px; padding-bottom: 9999px; border: 1px solid #ccc; border-left: none; } .item:first-child{ border-left: 1px solid #ccc; }</style><body> <ul class=“marign-layout”> <li class=“item”> 行业的发展必然带来职业的细分,在前端领域也一样,目前行业前端有下面这几个方向:界面展现用户体验和可访问性方向、偏后的js/nodejs开发方向、audio/video音视频富媒体方向、SVG/canvas/webGL动效创意表现与数据可视化方向、工具建设文档管理内部站建设的前端运维方向以及会议预定团建组织对外品牌宣传的前端运营方向。 </li> <li class=“item”> 由于每个人的性格特质,成长经历的差异,自然适合的方向也不一样。感性细腻有设计背景可以专注于用户体验,纯逻辑工程化思维浓郁则更适合偏后开发,数学物理强悍可以考虑数据可视化方向,沟通协调能力很强可以朝着前端运营方向努力。 </li> <li class=“item”> 在学生时代,大家都很纯粹,我喜欢这个就学这个,例如,很多女生很喜欢CSS,写页面,做效果。但是,等她开始找工作,还是接触真实的行业圈子的时候,事情就发生了变化。 </li> </ul></body>使用margin负值有一个缺点,如图:底部边框不见了,因为被父元素的overflow: hidden;切割掉了。 ...

January 5, 2019 · 1 min · jiezi

不怕你用不上!CSS 列表项布局技巧

编者按:为了不引入 HTML 标签自身的样式,本文未考虑 HTML 语义化的提倡,以使得行文与示例代码更加简洁。这是文章写作需要,而非代码风格示范。在开发中我们经常会遇到关于如何展示列表的问题,例如:图片选择器列表人员部门选择列表工作状态列表通用方法为了让其看起来更加舒适美观,通常我们会在每个列表项上添加 margin-right 和 margin-bottom 属性来隔开它们,然后一行超过容器长度后进行换行那么在各种情况下,如何处理列表项中margin-right和margin-bottom,让列表间隔和换行看起来更加自然美观是本篇的重点各种情况下的布局元素宽度已知,即知道每行最多多少个,且所有元素都在一个容器中思路:item 在一个容器中,每第三个去掉 margin-right,最后三个取消 margin-bottom(如最后一行不满 3 个也不影响)关键代码<div class=‘container’> <div class=‘item’>宽度已知,最多放三个</div> <div class=‘item’>宽度已知,最多放三个</div> <div class=‘item’>宽度已知,最多放三个</div> …</div><style>/* scss code /.container { .item { margin-right: 30px; margin-bottom: 20px; &:nth-child(3n) { margin-right: 0; } &:nth-last-child(-n+3) { margin-bottom: 0; } }}</style>运行截图完整代码:元素宽度已知,所有元素都在一个容器元素宽度已知 或 未知,且元素按照行数在相应容器中思路:最后一个 container 去掉 margin-bottom,最后一个 item 去掉 magin-right关键代码<div class=‘container’> <div class=‘item’></div> <div class=‘item’></div></div><div class=‘container’> <div class=‘item’></div> <div class=‘item’></div> <div class=‘item’></div></div><div class=‘container’> <div class=‘item’></div></div><style>/ scss code /.container { margin-bottom: 20px; &:last-child { margin-bottom: 0; } .item { margin-right: 30px; &:last-child { margin-right: 0; } }}</style>运行截图完整代码:元素宽度已知或未知,且按照行数在相应容器元素宽度未知,即不知道一行最多多少个,且所有元素都在一个容器中,常见于 flex 布局法1:Flex 布局思路:利用 flex 布局的 justify-content 主轴属性来控制元素的间距缺点:flex 虽然强大,但是面对 长度不定的列表项布局 还是不能很好满足要求关键代码<div class=‘container’> <div class=‘item’>两个可以成一行</div> <div class=‘item’>两个可以成一行</div> <div class=‘item’>这三个字</div> <div class=‘item’>独成一行呀独成一行呀独成一行呀独成</div> <div class=‘item’>两个才能成一行呀</div> <div class=‘item’>四个</div></div><style>/ scss code /.container { display: flex; flex-wrap: wrap; justify-content: space-between; / 可以尝试其他值,但效果仍不好 / .item { / margin-right: 30px; 可以不用 m-r,由 flex 来控制左右间距 / margin-bottom: 20px; }}运行截图完整代码:元素宽度已知或未知,且按照行数在相应容器法2:负margin接下来介绍 负margin 方法,可以很好的解决 长度不定的列表项布局 问题思路:用一个 wrapper 包在最外层,container 设置 负的 margin 来抵消 item 的 外边距参考链接:https://segmentfault.com/q/1010000005882081/a-1020000005894468关键代码<div class=“wrapper”> <div class=‘container’> <div class=‘item’>两个才能成一行呀</div> <div class=‘item’>两个才能成一行呀</div> <div class=‘item’>这三个字</div> <div class=‘item’>独成一行呀独成一行呀独成一行呀独</div> <div class=‘item’>两个才能成一行呀</div> <div class=‘item’>四个</div> </div></div><style>/ scss code */.wrapper { padding: 10px; border: 2px solid rgb(240, 103, 103); .container { display: flex; flex-wrap: wrap; margin-right: -30px; margin-bottom: -20px; .item { margin-right: 30px; margin-bottom: 20px; } }}</style>运行截图完整代码:法2:元素宽度未知,且所有元素都在一个容器总结多多利用 css3 属性来帮助我们更好的布局列表,避免使用 js 控制列表项,做到 css 与 js 解耦,更利于项目的维护以上可能未包含所有情况,欢迎提出或者分享其他更好的解决办法文 / Lawler61Learn and to learn.Github:https://github.com/lawler61作者博客:www.freeze61.me编 / 荧声本文由创宇前端作者授权发布,版权属于作者,创宇前端出品。欢迎注明出处转载本文。文章链接:https://knownsec-fed.com/2018…想要订阅更多来自知道创宇开发一线的分享,请搜索关注我们的微信公众号:创宇前端(KnownsecFED)。欢迎留言讨论,我们会尽可能回复。欢迎点赞、收藏、留言评论、转发分享和打赏支持我们。打赏将被完全转交给文章作者。感谢您的阅读。 ...

December 17, 2018 · 1 min · jiezi

关于CSS3 flex布局,这样简单做就好了。

flex布局在移动端会用得比较广泛,例如导航栏,菜单栏等,以支付宝,淘宝APP为例。看了网上很多关于flex布局,有些写得太乱了,也太复杂了。写一个导航栏,5个导航,用普通得写法是。<!DOCTYPE html><html><head> <title>Flex布局</title> <meta name=“viewport” content=“width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover”> <style type=“text/css”> *{margin:0px;padding: 0px;list-style: none;} .con{ height: 50px; text-align: center; background: #f00; } .con li{ width: 20%; border:1px solid #000; box-sizing: border-box; height: 50px; float: left; line-height: 50px; } </style></head><body><ul class=“con”><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul></body></html>就没每个<li></li>按照20%平均分,左浮动即可。这种方法是可以实现得,但后面再增加<li></li>,例如7个<li></li>,那么是下面这样的。很明显父级元素放不下了,只能被挤下去了。那么,用flex布局怎么做呢?有何优点?<!DOCTYPE html><html><head> <title>Flex布局</title> <meta name=“viewport” content=“width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover”> <style type=“text/css”> *{margin:0px;padding: 0px;list-style: none;} .con{ display: flex; height: 50px; text-align: center; background: #f00; } .con li{ flex: 1; text-align: center; line-height: 50px; border:1px solid #000; box-sizing: border-box; } </style></head><body><ul class=“con”><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul></body></html>效果是和上面一样的但是当我新增7-10个,都不会被挤下去,而是直接在父级元素上面进行排列并重新划分宽度。如果要加一个margin-right呢?这个也容易啊,但是最后一个<li></li>不贴边,我们要给最后一个<li></li>单独加一个style。<!DOCTYPE html><html><head> <title>Flex布局</title> <meta name=“viewport” content=“width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover”> <style type=“text/css”> *{margin:0px;padding: 0px;list-style: none;} .con{ display: flex; height: 50px; text-align: center; } .con li{ flex: 1; text-align: center; line-height: 50px; border:1px solid #000; box-sizing: border-box; margin-right: 3px; background: #f00; } </style></head><body><ul class=“con”><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li style=“margin-right: 0;">9</li></ul></body></html>下面再说一个不均等的。如上图,有三个<li></li>,每个<li></li>都是不均等的,在父级元素用普通的方法就是给不同的百分比。那么用flex布局,就是这样:<!DOCTYPE html><html><head> <title>Flex布局</title> <meta name=“viewport” content=“width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0,viewport-fit=cover”> <style type=“text/css”> *{margin:0px;padding: 0px;list-style: none;} .con{ display: flex; height: 50px; text-align: center; } .con .li_1{ flex: 1; text-align: center; line-height: 50px; border:1px solid #000; box-sizing: border-box; background: #f00; } .con .li_2{ flex: 2; text-align: center; line-height: 50px; border:1px solid #000; box-sizing: border-box; background: #f00; } .con .li_3{ flex: 3; text-align: center; line-height: 50px; border:1px solid #000; box-sizing: border-box; background: #f00; } </style></head><body><ul class=“con”><li class=“li_1”>1</li><li class=“li_2”>2</li><li class=“li_3”>3</li></ul></body></html>上面给每个<li></li>用flex划分,一共分6份,li_1占1/6,那就是flex:1;li_2占2/6,那就是flex:2;这个容易理解吧。下面接着说水平排列方式:在父级元素使用justify-contentjustify-content:space-around;/左右平均分布/justify-content:center;/居中/justify-content:space-between;/两端分布/justify-content:flex-start;/居左/justify-content:flex-end;/居右/Author:TANKINGWeChat:likeyunba520web:http://likeyunba.com ...

December 13, 2018 · 1 min · jiezi

Kubernetes 弹性伸缩全场景解析 (一)- 概念延伸与组件布局

传统弹性伸缩的困境弹性伸缩是Kubernetes中被大家关注的一大亮点,在讨论相关的组件和实现方案之前。首先想先给大家扩充下弹性伸缩的边界与定义,传统意义上来讲,弹性伸缩主要解决的问题是容量规划与实际负载的矛盾。如上图所示,蓝色的水位线表示集群的容量随着负载的提高不断的增长,红色的曲线表示集群的实际的负载真实的变化。而弹性伸缩要解决的就是当实际负载出现激增,而容量规划没有来得及反应的场景。常规的弹性伸缩是基于阈值的,通过设置一个资源缓冲水位来保障资源的充盈,通常15%-30%左右的资源预留是比较常见的选择。换言之就是通过一个具备缓冲能力的资源池用资源的冗余换取集群的可用。这种方式表面上看是没有什么问题的,确实在很多的解决方案或者开源组件中也是按照这种方式进行实现的,但是当我们深入的思考这种实现方案的时候会发现,这种方式存在如下三个经典问题。1. 百分比碎片难题在一个Kubernetes集群中,通常不只包含一种规格的机器,针对不同的场景、不同的需求,机器的配置、容量可能会有非常大的差异,那么集群伸缩时的百分比就具备非常大的迷惑性。假设我们的集群中存在4C8G的机器与16C32G的机器两种不同规格,对于10%的资源预留,这两种规格是所代表的意义是完全不同的。特别是在缩容的场景下,通常为了保证缩容后的集群不处在震荡状态,我们会一个节点一个节点或者二分法来缩容节点,那么如何根据百分比来判断当前节点是处在缩容状态就尤为重要,此时如果大规格机器有较低的利用率被判断缩容,那么很有可能会造成节点缩容后,容器重新调度后的争抢饥饿。如果添加判断条件,优先缩容小配置的节点,则有可能造成缩容后资源的大量冗余,最终集群中可能会只剩下所有的巨石节点。2. 容量的规划炸弹还记得在没有使用容器前,是如何做容量规划的吗?一般会按照应用来进行机器的分配,例如,应用A需要2台4C8G的机器,应用B需要4台8C16G的机器,应用A的机器与应用B的机器是独立的,相互不干扰。到了容器的场景中,大部分的开发者无需关心底层的资源了,那么这个时候容量规划哪里去了呢?在Kubernetes中是通过Request和Limit的方式进行设置,Request表示资源的申请值,Limit表示资源的限制值。既然Request和Limit才是容量规划的对等概念,那么这就代表着资源的实际计算规则要根据Request和Limit才更加准确。而对于每个节点预留资源阈值而言,很有可能会造成小节点的预留无法满足调度,大节点的预留又调度不完的场景。3. 资源利用率困境集群的资源利用率是否可以真的代表当前的集群状态呢?当一个Pod的资源利用率很低的时候,不代表就可以侵占他所申请的资源。在大部分的生产集群中,资源利用率都不会保持在一个非常高的水位,但从调度来讲,资源的调度水位应该保持在一个比较高的水位。这样才能既保证集群的稳定可用,又不过于浪费资源。如果没有设置Request与Limit,而集群的整体资源利用率很高这意味着什么?这表示所有的Pod都在被以真实负载为单元进行调度,相互之间存在非常严重的争抢,而且简单的加入节点也丝毫无法解决问题,因为对于一个已调度的Pod而言,除了手动调度与驱逐没有任何方式可以将这个Pod从高负载的节点中移走。那如果我们设置了Request与Limit而节点的资源利用率又非常高的时候说明了什么呢?很可惜这在大部分的场景下都是不可能的,因为不同的应用不同的负载在不同的时刻资源的利用率也会有所差异,大概率的情况是集群还没有触发设置的阈值就已经无法调度Pod了。弹性伸缩概念的延伸既然基于资源利用率的弹性伸缩有上述已知的三个问题,有什么办法可以来解决呢?随着应用类型的多样性发展,不同类型的应用的资源要求也存在越来越大的差异。弹性伸缩的概念和意义也在变化,传统理解上弹性伸缩是为了解决容量规划和在线负载的矛盾,而现在是资源成本与可用性之间的博弈。如果将常见的应用进行规约分类,可以分为如下四种不同类型:在线任务类型比较常见的是网站、API服务、微服务等常见的互联网业务型应用,这种应用的特点是对常规资源消耗较高,比如CPU、内存、网络IO、磁盘IO等,对于业务中断容忍性差。离线任务类型例如大数据离线计算、边缘计算等,这种应用的特点是对可靠性的要求较低,也没有明确的时效性要求,更多的关注点是成本如何降低。定时任务类型定时运行一些批量计算任务是这种应用的比较常见形态,成本节约与调度能力是重点关注的部分。特殊任务类型例如闲时计算的场景、IOT类业务、网格计算、超算等,这类场景对于资源利用率都有比较高的要求。单纯的基于资源利用率的弹性伸缩大部分是用来解决第一种类型的应用而产生的,对于其他三种类型的应用并不是很合适,那么Kubernetes是如何解决这个问题的呢?kubernetes的弹性伸缩布局Kubernetes将弹性伸缩的本质进行了抽象,如果抛开实现的方式,对于不同应用的弹性伸缩而言,该如何统一模型呢?Kubernetes的设计思路是将弹性伸缩划分为保障应用负载处在容量规划之内与保障资源池大小满足整体容量规划两个层面。简单理解,当需要弹性伸缩时,优先变化的应该是负载的容量规划,当集群的资源池无法满足负载的容量规划时,再调整资源池的水位保证可用性。而两者相结合的方式是无法调度的Pod来实现的,这样开发者就可以在集群资源水位较低的时候使用HPA、VPA等处理容量规划的组件实现实时极致的弹性,资源不足的时候通过Cluster-Autoscaler进行集群资源水位的调整,重新调度,实现伸缩的补偿。两者相互解耦又相互结合,实现极致的弹性。在Kubernetes的生态中,在多个维度、多个层次提供了不同的组件来满足不同的伸缩场景。如果我们从伸缩对象与伸缩方向两个方面来解读Kubernetes的弹性伸缩的话,可以得到如下的弹性伸缩矩阵。cluster-autoscaler:kubernetes社区中负责节点水平伸缩的组件,目前处在GA阶段(General Availability,即正式发布的版本)。HPA:kubernetes社区中负责Pod水平伸缩的组件,是所有伸缩组件中历史最悠久的,目前支持autoscaling/v1、autoscaling/v2beta1与autoscaling/v2beta2,其中autoscaling/v1只支持CPU一种伸缩指标,在autoscaling/v2beta1中增加支持custom metrics,在autoscaling/v2beta2中增加支持external metrics。cluster-proportional-autoscaler:根据集群的节点数目,水平调整Pod数目的组件,目前处在GA阶段。vetical-pod-autoscaler:根据Pod的资源利用率、历史数据、异常事件,来动态调整负载的Request值的组件,主要关注在有状态服务、单体应用的资源伸缩场景,目前处在beta阶段。addon-resizer:根据集群中节点的数目,纵向调整负载的Request的组件,目前处在beta阶段。在这五个组件中,cluster-autoscaler、HPA、cluster-proportional-autoscaler是目前比较稳定的组件,建议有相关需求的开发者进行选用。对于百分之八十以上的场景,我们建议客户通过HPA结合cluster-autoscaler的方式进行集群的弹性伸缩管理,HPA负责负载的容量规划管理而cluster-autoscaler负责资源池的扩容与缩容。最后在本文中,和大家主要讨论的是在云原生时代下弹性伸缩概念的延伸,以及Kubernetes社区是如何通过解耦的方式通过多个转职的组件实现了两个维度的弹性伸缩,在本系列后面的文章中会为一一解析每个弹性伸缩组件的相关原理与用法。本文作者:莫源阅读原文本文为云栖社区原创内容,未经允许不得转载。

November 23, 2018 · 1 min · jiezi

Texture 布局篇

Texture 拥有自己的一套成熟布局方案,虽然学习成本略高,但至少比原生的 AutoLayout 写起来舒服,重点是性能远好于 AutoLayout ,Texture 文档上也指出了这套布局方案的的优点:Fast: As fast as manual layout code and significantly faster than Auto LayoutAsynchronous & Concurrent: Layouts can be computed on background threads so user interactions are not interrupted.Declarative: Layouts are declared with immutable data structures. This makes layout code easier to develop, document, code review, test, debug, profile, and maintain.Cacheable: Layout results are immutable data structures so they can be precomputed in the background and cached to increase user perceived performance.Extensible: Easy to share code between classes.首先这套布局都是基于 Texture 组件的,所以当遇到要使用原生控件时,通过用 block 的方式包装一个原生组件再合适不过了,例如:ASDisplayNode *animationImageNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull{ FLAnimatedImageView *animationImageView = [[FLAnimatedImageView alloc] init]; animationImageView.layer.cornerRadius = 2.0f; animationImageView.clipsToBounds = YES; return animationImageView;}];[self addSubnode:animationImageNode];self.animationImageNode = animationImageNode;ASDisplayNode 在初始化之后会检查是否有子视图,如果有就会调用- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize方法进行布局,所以对视图进行布局需要重写这个方法。看一个例子:- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ ASInsetLayoutSpec *inset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:_childNode]; return insetLayout;}_childNode 相对于父视图边距都为 0,也就是AutoLayout中 top bottom left right 都为 0。—————————–父视图—————————-| ————————-_childNode——————— || | | || | | || ————————— ————————— |————————————————————–可以看到layoutSpecThatFits:方法返回的必须是 ASLayoutSpec, ASInsetLayoutSpec 是它的子类之一,下面是所有的子类及其关系:ASLayoutSpecASAbsoluteLayoutSpec // 绝对布局ASBackgroundLayoutSpec // 背景布局ASInsetLayoutSpec // 边距布局ASOverlayLayoutSpec // 覆盖布局ASRatioLayoutSpec // 比例布局ASRelativeLayoutSpec // 顶点布局ASCenterLayoutSpec // 居中布局ASStackLayoutSpec // 盒子布局ASWrapperLayoutSpec // 填充布局ASCornerLayoutSpec // 角标布局_ASAbsoluteLayoutSpec使用方法和原生的绝对布局类似- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ self.childNode.style.layoutPosition = CGPointMake(100, 100); self.childNode.style.preferredLayoutSize = ASLayoutSizeMake(ASDimensionMake(100), ASDimensionMake(100)); ASAbsoluteLayoutSpec *absoluteLayout = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[self.childNode]]; return absoluteLayout;}值得提的是:ASAbsoluteLayoutSpec 一般情况都会通过 ASOverlayLayoutSpec 或 ASOverlayLayoutSpec 着陆,因为只有上述两种布局才能保留 ASAbsoluteLayoutSpec 绝对布局的事实。举个例子当视图中只有一个控件需要用的是 ASAbsoluteLayoutSpec 布局,而其他控件布局用的是 ASStackLayoutSpec(后面会介绍),那么一旦 absoluteLayout 被加入到 ASStackLayoutSpec 也就失去它原本的布局的意义。ASOverlayLayoutSpec *contentLayout = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:stackLayout overlay:absoluteLayout];不过官方文档明确指出应该尽量少用这种布局方式:Absolute layouts are less flexible and harder to maintain than other types of layouts._ASBackgroundLayoutSpec- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ ASBackgroundLayoutSpec *backgroundLayout = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:self.childNodeB background:self.childNodeA]; return backgroundLayout;}把childNodeA 做为 childNodeB 的背景,也就是 childNodeB 在上层,要注意的是 ASBackgroundLayoutSpec 事实上根本不会改变视图的层级关系,比如:ASDisplayNode *childNodeB = [[ASDisplayNode alloc] init];childNodeB.backgroundColor = [UIColor blueColor];[self addSubnode:childNodeB];self.childNodeB = childNodeB;ASDisplayNode *childNodeA = [[ASDisplayNode alloc] init];childNodeA.backgroundColor = [UIColor redColor];[self addSubnode:childNodeA];self.childNodeA = childNodeA;那么即使使用上面的布局方式,childNodeB 依然在下层。_ASInsetLayoutSpec比较常用的一个类,看图应该能一目了然(图片来自于官方文档)- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ ASInsetLayoutSpec *inset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:_childNode]; return insetLayout;}_childNode 相对于父视图边距都为 0,相当于填充整个父视图。它和之后会说到的ASOverlayLayoutSpec 实际上更多的用来组合两个 Element 而已。ASOverlayLayoutSpec参考 ASBackgroundLayoutSpec_ASRatioLayoutSpec(图片来自于官方文档)也是比较常用的一个类,作用是设置自身的高宽比,例如设置正方形的视图- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ ASRatioLayoutSpec *ratioLayout = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0f child:self.childNodeA]; return ratioLayout;}ASRelativeLayoutSpec把它称为顶点布局可能有点不恰当,实际上它可以把视图布局在:左上、左下、右上、右下四个顶点以外,还可以设置成居中布局。- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ self.childNodeA.style.preferredSize = CGSizeMake(100, 100); ASRelativeLayoutSpec *relativeLayout = [ASRelativeLayoutSpec relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionEnd verticalPosition:ASRelativeLayoutSpecPositionStart sizingOption:ASRelativeLayoutSpecSizingOptionDefault child:self.childNodeA]; return relativeLayout;}上面的例子就是把 childNodeA 显示在右上角。ASCenterLayoutSpec绝大多数情况下用来居中显示视图- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ self.childNodeA.style.preferredSize = CGSizeMake(100, 100); ASCenterLayoutSpec *relativeLayout = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:self.childNodeA]; return relativeLayout;}ASStackLayoutSpec可以说这是最常用的类,而且相对于其他类来说在功能上是最接近于 AutoLayout 的。之所以称之为盒子布局是因为它和 CSS 中 Flexbox 很相似,关于 Flexbox 的可以看下阮一峰的这篇文章。先看一个例子:- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ self.childNodeA.style.preferredSize = CGSizeMake(100, 100); self.childNodeB.style.preferredSize = CGSizeMake(200, 200); ASStackLayoutSpec *stackLayout = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:12 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[self.childNodeA, self.childNodeB]]; return stackLayout;}简单的说明下各个参数的作用:direction:主轴的方向,有两个可选值:纵向:ASStackLayoutDirectionVertical横向:ASStackLayoutDirectionHorizontalspacing: 主轴上视图排列的间距,比如有四个视图,那么它们之间的存在三个间距值都应该是spacingjustifyContent: 主轴上的排列方式,有五个可选值:ASStackLayoutJustifyContentStart 从前往后排列ASStackLayoutJustifyContentCenter 居中排列ASStackLayoutJustifyContentEnd 从后往前排列ASStackLayoutJustifyContentSpaceBetween 间隔排列,两端无间隔ASStackLayoutJustifyContentSpaceAround 间隔排列,两端有间隔alignItems: 交叉轴上的排列方式,有五个可选值:ASStackLayoutAlignItemsStart 从前往后排列ASStackLayoutAlignItemsEnd 从后往前排列ASStackLayoutAlignItemsCenter 居中排列ASStackLayoutAlignItemsStretch 拉伸排列ASStackLayoutAlignItemsBaselineFirst 以第一个文字元素基线排列(主轴是横向才可用)ASStackLayoutAlignItemsBaselineLast 以最后一个文字元素基线排列(主轴是横向才可用)children: 包含的视图。数组内元素顺序同样代表着布局时排列的顺序,所以需要注意主轴的方向设置尤为重要,如果主轴设置的是 ASStackLayoutDirectionVertical, 那么 justifyContent 各个参数的意义就是:ASStackLayoutJustifyContentStart 从上往下排列ASStackLayoutJustifyContentCenter 居中排列ASStackLayoutJustifyContentEnd 从下往上排列ASStackLayoutJustifyContentSpaceBetween 间隔排列,两端无间隔ASStackLayoutJustifyContentSpaceAround 间隔排列,两端有间隔alignItems 就是:ASStackLayoutAlignItemsStart 从左往右排列ASStackLayoutAlignItemsEnd 从右往左排列ASStackLayoutAlignItemsCenter 居中排列ASStackLayoutAlignItemsStretch 拉伸排列ASStackLayoutAlignItemsBaselineFirst 无效ASStackLayoutAlignItemsBaselineLast 无效对于子视图间距不一样的布局方法,后面实战中会讲到。ASWrapperLayoutSpec填充整个视图- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ ASWrapperLayoutSpec *wrapperLayout = [ASWrapperLayoutSpec wrapperWithLayoutElement:self.childNodeA]; return wrapperLayout;}ASCornerLayoutSpec顾名思义 ASCornerLayoutSpec 适用于类似于角标的布局override func layoutSpecThatFits( constrainedSize: ASSizeRange) -> ASLayoutSpec{ let cornerSpec = ASCornerLayoutSpec(child: avatarNode, corner: badgeNode, location: .topRight) cornerSpec.offset = CGPoint(x: -3, y: 3)}最需要注意的是offset是控件的Center的偏移布局实战案例一简单的文件覆盖在图片上,文字居中。- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ ASWrapperLayoutSpec *wrapperLayout = [ASWrapperLayoutSpec wrapperWithLayoutElement:self.coverImageNode]; ASCenterLayoutSpec *centerSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:self.textNode]; ASOverlayLayoutSpec *overSpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:wrapperLayout overlay:centerSpec]; return overSpec;}ASWrapperLayoutSpec 把图片铺满整个视图ASCenterLayoutSpec 把文字居中显示ASOverlayLayoutSpec 把文字覆盖到图片上注意第三步就是之前提到的 ASOverlayLayoutSpec/ASBackgroundLayoutSpec 的作用:用于组合两个 Element。案例二这个是轻芒阅读(豌豆荚一览) APP 内 AppSo 频道 Cell 的布局,应该也是比较典型的布局之一。为了方便理解先给各个元素定一下名称,从上至下,从左往右分别是:coverImageNode // 大图titleNode // 标题subTitleNode // 副标题dateTextNode // 发布时间shareImageNode // 分享图标shareNumberNode // 分享数量likeImageNode // 喜欢图标likeNumberNode // 喜欢数量- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{ self.shareImageNode.style.preferredSize = CGSizeMake(15, 15); self.likeImageNode.style.preferredSize = CGSizeMake(15, 15); ASStackLayoutSpec *likeLayout = [ASStackLayoutSpec horizontalStackLayoutSpec]; likeLayout.spacing = 4.0; likeLayout.justifyContent = ASStackLayoutJustifyContentStart; likeLayout.alignItems = ASStackLayoutAlignItemsCenter; likeLayout.children = @[self.likeImageNode, self.likeNumberNode]; ASStackLayoutSpec *shareLayout = [ASStackLayoutSpec horizontalStackLayoutSpec]; shareLayout.spacing = 4.0; shareLayout.justifyContent = ASStackLayoutJustifyContentStart; shareLayout.alignItems = ASStackLayoutAlignItemsCenter; shareLayout.children = @[self.shareImageNode, self.shareNumberNode]; ASStackLayoutSpec *otherLayout = [ASStackLayoutSpec horizontalStackLayoutSpec]; otherLayout.spacing = 12.0; otherLayout.justifyContent = ASStackLayoutJustifyContentStart; otherLayout.alignItems = ASStackLayoutAlignItemsCenter; otherLayout.children = @[likeLayout, shareLayout]; ASStackLayoutSpec *bottomLayout = [ASStackLayoutSpec horizontalStackLayoutSpec]; bottomLayout.justifyContent = ASStackLayoutJustifyContentSpaceBetween; bottomLayout.alignItems = ASStackLayoutAlignItemsCenter; bottomLayout.children = @[self.dateTextNode, otherLayout]; self.titleNode.style.spacingBefore = 12.0f; self.subTitleNode.style.spacingBefore = 16.0f; self.subTitleNode.style.spacingAfter = 20.0f; ASRatioLayoutSpec *rationLayout = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:0.5 child:self.coverImageNode]; ASStackLayoutSpec *contentLayout = [ASStackLayoutSpec horizontalStackLayoutSpec]; contentLayout.justifyContent = ASStackLayoutJustifyContentStart; contentLayout.alignItems = ASStackLayoutAlignItemsStretch; contentLayout.children = @[ rationLayout, self.titleNode, self.subTitleNode, bottomLayout ]; ASInsetLayoutSpec *insetLayout = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(16, 16, 16, 16) child:contentLayout]; return insetLayout;}下面详细解释下布局,不过首先要明确的是,Texture 的这套布局方式遵守从里到外的布局原则,使用起来才会得心应手。根据布局的原则,首先利用 ASStackLayoutSpec 布局 分享图标 和 分享数量、 喜欢图标 和 喜欢数量。还是通过 ASStackLayoutSpec 包装第一步的两个的布局得到 otherLayout 布局对象。依然是 ASStackLayoutSpec 包装otherLayout和 发布时间。注意这里设置横向的排列方式 ASStackLayoutJustifyContentSpaceBetween已到达两端布局的目的,最终返回 bottomLayout。由于 大图 是网络图片,对于 Cell 来说,子视图的布局必能能决定其高度(Cell 宽度是默认等于 TableNode 的宽度),所以这里必须设置 大图 的高度,ASRatioLayoutSpec 设置了图片的高宽比。接下来布局应该就是 大图、标题、副标题、bottomLayout 的一个纵向布局,可以发现这里的视图间距并不相同,这时候 spacingBefore 和 spacingAfter 就会很有用,它们用来分别设置元素在主轴上的前后间距。self.titleNode.style.spacingBefore = 12.0f; 意思就是 标题 相对于 大图 间距为 12。最后通过一个 ASInsetLayoutSpec 设置一个边距。可以看到不仅是 Node,ASLayoutSpec 本身也可以作为布局元素,这是因为只要是遵守了 <ASLayoutElement> 协议的对象都可以作为布局元素。案例三 override func layoutSpecThatFits( constrainedSize: ASSizeRange) -> ASLayoutSpec { self.node1.style.preferredSize = CGSize(width: constrainedSize.max.width, height: 136) self.node2.style.preferredSize = CGSize(width: 58, height: 25) self.node2.style.layoutPosition = CGPoint(x: 14.0, y: 95.0) self.node3.style.height = ASDimensionMake(37.0) self.node4.style.preferredSize = CGSize(width: 80, height: 20) self.node5.style.preferredSize = CGSize(width: 80, height: 20) self.node4.style.spacingBefore = 14.0 self.node5.style.spacingAfter = 14.0 let absoluteLayout = ASAbsoluteLayoutSpec(children: [self.node2]) let overlyLayout = ASOverlayLayoutSpec(child: self.node1, overlay: absoluteLayout) let insetLayout = ASInsetLayoutSpec(insets: UIEdgeInsetsMake(0, 14, 0, 14), child: self.node3) insetLayout.style.spacingBefore = 13.0 insetLayout.style.spacingAfter = 25.0 let bottomLayout = ASStackLayoutSpec.horizontal() bottomLayout.justifyContent = .spaceBetween bottomLayout.alignItems = .start bottomLayout.children = [self.node4, self.node5] bottomLayout.style.spacingAfter = 10.0// bottomLayout.style.width = ASDimensionMake(constrainedSize.max.width) let stackLayout = ASStackLayoutSpec.vertical() stackLayout.justifyContent = .start stackLayout.alignItems = .stretch stackLayout.children = [overlyLayout, insetLayout, bottomLayout] return stackLayout }为了演示 ASAbsoluteLayoutSpec 的使用,这里 node3 我们用 ASAbsoluteLayoutSpec 布局。接下来说下要点:node 和 layoutSpec 都可以设置 style 属性,因为它们都准守 ASLayoutElement 协议当 spaceBetween 没有达到两端对齐的效果,尝试设置当前 layoutSpec 的 width(如注释)或它的上一级布局对象的 alignItems,在例子中就是 stackLayout.alignItems = .stretchASAbsoluteLayoutSpec 必须有落点(除非是只有绝对布局),例子中 ASAbsoluteLayoutSpec 着落点就在 ASOverlayLayoutSpec案例四此案例主要为了演示 flexGrow 的用法,先介绍下 flexGrow 的作用(来自于简书九彩拼盘)该属性来设置,当父元素的宽度大于所有子元素的宽度的和时(即父元素会有剩余空间),子元素如何分配父元素的剩余空间。flex-grow的默认值为0,意思是该元素不索取父元素的剩余空间,如果值大于0,表示索取。值越大,索取的越厉害。举个例子:父元素宽400px,有两子元素:A和B。A宽为100px,B宽为200px,则空余空间为 400-(100+200)= 100px。如果A,B都不索取剩余空间,则有100px的空余空间。如果A索取剩余空间:设置flex-grow为1,B不索取。则最终A的大小为 自身宽度(100px)+ 剩余空间的宽度(100px)= 200px如果A,B都设索取剩余空间,A设置flex-grow为1,B设置flex-grow为2。则最终A的大小为 自身宽度(100px)+ A获得的剩余空间的宽度(100px (1/(1+2))),最终B的大小为 自身宽度(200px)+ B获得的剩余空间的宽度(100px (2/(1+2))) override func layoutSpecThatFits( constrainedSize: ASSizeRange) -> ASLayoutSpec { self.node1.style.height = ASDimensionMake(20.0) var imageLayoutArray = ASLayoutElement [self.node2, self.node3, self.node4].forEach { (node) in let layout = ASRatioLayoutSpec(ratio: 2.0/3.0, child: node) layout.style.flexGrow = 1 // 相当于宽度相等 imageLayoutArray.append(layout) } let imageLayout = ASStackLayoutSpec.horizontal() imageLayout.justifyContent = .start imageLayout.alignItems = .start imageLayout.spacing = 14.0 imageLayout.children = imageLayoutArray let contentLayout = ASStackLayoutSpec.vertical() contentLayout.justifyContent = .start contentLayout.alignItems = .stretch contentLayout.spacing = 22.0 contentLayout.children = [self.node1, imageLayout] return ASInsetLayoutSpec(insets: UIEdgeInsetsMake(22.0, 16.0, 22.0, 16.0), child: contentLayout) }在这个案例中 node2、node3、node4 的宽度的总和小于父元素的宽度,所以为了达到宽度相同只需要设置三者的 flexGrow 相同就行(都为1),再通过 ASRatioLayoutSpec 固定各自的宽高比,那么对于这个三个控件来说最终的宽度是确定的。案例四此案例主要为了演示 flexShrink 的用法,同样还来自于简书九彩拼盘关于 flexShrink 的介绍该属性来设置,当父元素的宽度小于所有子元素的宽度的和时(即子元素会超出父元素),子元素如何缩小自己的宽度的。flex-shrink的默认值为1,当父元素的宽度小于所有子元素的宽度的和时,子元素的宽度会减小。值越大,减小的越厉害。如果值为0,表示不减小。举个例子:父元素宽400px,有两子元素:A和B。A宽为200px,B宽为300px。则A,B总共超出父元素的宽度为(200+300)- 400 = 100px。如果A,B都不减小宽度,即都设置flex-shrink为0,则会有100px的宽度超出父元素。如果A不减小宽度:设置flex-shrink为0,B减小。则最终B的大小为 自身宽度(300px)- 总共超出父元素的宽度(100px)= 200px如果A,B都减小宽度,A设置flex-shirk为3,B设置flex-shirk为2。则最终A的大小为 自身宽度(200px)- A减小的宽度(100px (200px 3/(200 3 + 300 2))) = 150px,最终B的大小为 自身宽度(300px)- B减小的宽度(100px (300px 2/(200 3 + 300 2))) = 250px目前关于该属性最常见还是用于对文本的宽度限制,在上图中 textNode 和 displayNode 是两端对齐,而且需要限制文本的最大宽度,这时候设置 flexShrink 是最方便的。override func layoutSpecThatFits( constrainedSize: ASSizeRange) -> ASLayoutSpec { self.displayNode.style.preferredSize = CGSize(width: 42.0, height: 18.0) self.textNode.style.flexShrink = 1 let contentLayout = ASStackLayoutSpec.horizontal() contentLayout.justifyContent = .spaceBetween contentLayout.alignItems = .start contentLayout.children = [self.textNode, self.displayNode] let insetLayout = ASInsetLayoutSpec(insets: UIEdgeInsetsMake(16.0, 16.0, 16.0, 16.0), child: contentLayout) return insetLayout }随便提一下的是如果 ASTextNode 出现莫名的文本截断问题,可以用 ASTextNode2 代替。案例五还算比较典型的例子override func layoutSpecThatFits( constrainedSize: ASSizeRange) -> ASLayoutSpec { let otherLayout = ASInsetLayoutSpec(insets: UIEdgeInsetsMake(10.0, 10.0, CGFloat(Float.infinity), CGFloat(Float.infinity)), child: topLeftNode) let contentLayout = ASOverlayLayoutSpec(child: coverImageNode, overlay: otherLayout) return contentLayout }利用 ASInsetLayoutSpec 是最好的解决方案,值得注意的是对于红色控件只需要设置向上和向左的间距,那么其他方向的可以用 CGFloat(Float.infinity) 代替,并不需要给出具体数值。 ...

November 23, 2018 · 5 min · jiezi

聊聊Flexbox布局中的flex的演算法

到目前为止,Flexbox布局应该是目前最流行的布局方式之一了。而Flexbox布局的最大特性就是让Flex项目可伸缩,也就是让Flex项目的宽度和高度可以自动填充Flex容器剩余的空间或者缩小Flex项目适配Flex容器不足的宽度。而这一切都是依赖于Flexbox属性中的flex属性来完成。一个Flex容器会等比的按照各Flex项目的扩展比率分配Flex容器剩余空间,也会按照收缩比率来缩小各Flex项目,以免Flex项目溢出Flex容器。但其中Flex项目又是如何计算呢?他和扩展比率或收缩比率之间又存在什么关系呢?在这篇文章中我们将一起来探来。在Flexbox布局中,容器中显示式使用display设置为flex或inline-flex,那么该容器就是Flex容器,而该容器的所有子元素就是Flex项目。简介在这篇文章中,我们将要聊的是有关于flex属性的事情,特别是如何使用该属性来计算Flex项目?在开始之前,先来简单的了解一下flex属性。在Flexbox中,flex属性是flex-grow(扩展比率)、flex-shrink(收缩比率)和flex-basis(伸缩基准)三个属性的简称。这三个属性可以控制一个Flex项目(也有人称为Flex元素),主要表现在以下几个方面:flex-grow:Flex项目的扩展比率,让Flex项目得到(伸张)多少Flex容器多余的空间(Positive free space)flex-shrink:Flex项目收缩比率,让Flex项目减去Flex容器不足的空间(Negative free space)flex-basis:Flex项目未扩展或收缩之前,它的大小是多少在Flexbox布局中,只有充分理解了这三个属性才能彻底的掌握Flex项目是如何扩展和收缩的,也才能更彻底的掌握Flexbox布局。因此掌握这三个属性,以及他们之间的计算关系才是掌握Flexbox布局的关键所在。相关概念在具体介绍flex相关的技术之前,先对几个概念进行描述,因为理解了这几个概念更有易于大家对后面知识的理解。主轴长度和主轴长度属性Flex项目在主轴方向的宽度或高度就是Flex项目的主轴长度,Flex项目的主轴长度属性是width或height属性,具体是哪一个属性,将会由主轴方向决定。剩余空间和不足空间在Flexbox布局中,Flex容器中包含一个或多个Flex项目(该容器的子元素或子节点)。Flex容器和Flex项目都有其自身的尺寸大小,那么就会有:Flex项目尺寸大小之和大于或小于Flex容器 情景:当所有Flex项目尺寸大小之和小于Flex容器时,Flex容器就会有多余的空间没有被填充,那么这个空间就被称为Flex容器的剩余空间(Positive Free Space)当所有Flex项目尺寸大小之和大于Flex容器时,Flex容器就没有足够的空间容纳所有Flex项目,那么多出来的这个空间就被称为负空间(Negative Free Space)举个例子向大家阐述这两个情形:“假设我们有一个容器(Flex容器),显式的给其设置了width为800px,padding为10px,并且box-sizing设置为border-box”。根据CSS的盒模型原理,我们可以知道Flex容器的内宽度(Content盒子的宽度)为800px - 10px * 2 = 780px:假设Flex容器中包含了四个Flex项目,而且每个Flex项目的width都为100px,那么所有Flex项目的宽度总和则是100px * 4 = 400px(Flex项目没有设置其他任何有关于盒模型的尺寸),那么Flex容器将会有剩余的空间出来,即780px - 400px = 380px。这个380px就是我们所说的Flex容器的剩余空间:假设把Flex项目的width从100px调到300px,那么所有Flex项目的宽度总和就变成了300px * 4 = 1200px。这个时候Flex项目就溢出了Flex容器,这个溢出的宽度,即1200px - 780px = 420px。这个420px就是我们所说的Flex容器的不足空间:上面演示的是主轴在x轴方向,如果主轴变成y轴的方向,同样存在上述两种情形,只不过把width变成了height。接下来的内容中,如果没有特殊说明,那么所看到的示例都仅演示主轴在x轴的方向,即flex-direction为row!min-content 和 max-contentmin-content和max-content是CSS中的一个新概念,隶属于CSS Intrinsic and Extrinsic Sizing Specification模块。简单的可以这么理解。CSS可以给任何一个元素显式的通过width属性指定元素内容区域的宽度,内容区域在元素padding、border和margin里面。该属性也是CSS盒模型众多属性之一。记住,CSS的box-sizing可以决定width的计算方式。如果我们显式设置width为关键词auto时,元素的width将会根据元素自身的内容来决定宽度。而其中的min-content和max-content也会根据元素的内容来决定宽度,只不过和auto有较大的差异min-content: 元素固有的最小宽度max-content: 元素固有的首选宽度比如下面这个示例:如果内容是英文的话,min-content的宽度将取决于内容中最长的单词宽度,中文就有点怪异(其中之因目前并未深究),而max-content则会计算内容排整行的宽度,有点类似于加上了white-space:nowrap一样。上例仅展示了min-content和max-content最基本的渲染效果(Chrome浏览器渲染行为)。这里不做深入的探讨论,毕竟不是本文的重点,如果感兴趣,欢迎关注后续的相关更新,或者先阅读@张鑫旭 老师写的一篇文章《理解CSS3 max/min-content及fit-content等width值》回到我们自己的主题上来。前面在介绍Flex剩余空间和不足空间的时候,我们可以得知,出现这两种现象取决于Flex容器和Flex项目的尺寸大小。而flex属性可以根据Flex容器的剩余空间(或不足空间)对Flex项目进行扩展(或收缩)。那么为了计算出有多少Flex容器的剩余空间能用于Flex项目上,客户端(浏览器)就必须知道Flex项目的尺寸大小。要是没有显式的设置元素的width属性,那么问题就来了,浏览器它是如何解决没有应用于绝对单位的宽度(或高度)的Flex项目,即如何计算?这里所说的min-content和max-content两个属性值对于我们深入的探讨flex属性中的flex-grow和 flex-grow属性有一定的影响。所以提前向大家简单的阐述一正是这两个属性值在浏览器中的渲染行为。简单的总结一下:min-content的大小,从本质上讲,是由字符串中最长的单词决定了大小;max-content则和min-content想反. 它会变得尽可能大, 没有自动换行的机会。如果Flex容器太窄, 它就会溢出其自身的盒子!Flex项目的计算在Flexbox布局当中,其中 flex-grow、flex-shrink和flex-basis都将会影响Flex项目的计算。接下来我们通过一些简单的示例来阐述这方面的知识。flex-basisflex-basis属性在任何空间分配发生之前初始化Flex项目的尺寸。其默认值为auto。如果flex-basis的值设置为auto,浏览器将先检查Flex项目的主尺寸是否设置了绝对值再计算出Flex项目的初始值。比如说,你给Flex项目设置的width为200px,那么200px就是Flex项目的flex-basis值。如果你的Flex项目可以自动调整大小,则auto会解析为其内容的大小,这个时候,min-content和max-content变会起作用。此时将会把Flex项目的max-content作为 flex-basise的值。比如,下面这样的一个简单示例:flex-grow和flex-shrink的值都为0,第一个Flex项目的width为150px,相当于flex-basis的值为150px,而另外两个Flex项目在没有设置宽度的情况之下,其宽度由内容的宽度来设置。如果flex-basis的值设置为关键词content,会导致Flex项目根据其内容大小来设置Flex项目,叧怕是Flex项目显式的设置了width的值。到目前为止,content还未得到浏览器很好的支持。flex-basis除了可以设置auto、content、fill、max-content、min-content和fit-content关键词之外,还可以设置<length>值。如果<length>值是一个百分比值,那么Flex项目的大小将会根据Flex容器的width进行计算。比如下面这个示例:Flex容器显式设置了width(和box-sizing取值有关系,上图为border-box的示例结果),那么flex-basis会根据Flex容器的width计算出来,如果Flex容器未显示设置width值,则计算出来的结果将是未定义的(会自动根据Flex容器的宽度进行计算)。在Flexbox布局中,如果你想完全忽略Flex项目的尺寸,则可以将flex-basis设置为0。这样的设置,基本上是告诉了浏览器,Flex容器所有空间都可以按照相关的比例进行分配。来看一个简单的示例,Flex项目未显式设置width情况之下,flex-basis不同取值的渲染效果。到写这篇文章为止,使用Firefox浏览器查看效果更佳。当Flex项目显式的设置了min-width或max-width的值时,就算Flex项目显式的设置了flex-basis的值,也会按min-width和max-width设置Flex项目宽度。当计算的值大于max-width时,则按max-width设置Flex项目宽度;当计算的值小于min-width时,则按min-width设置Flex项目宽度:有关于flex-basis属性相关的运用简单的小结一下:flex-basis默认值为auto如果Flex项目显式的设置了width值,同时flex-basis为auto时,则Flex项目的宽度为按width来计算,如果未显式设置width,则按Flex项目的内容宽度来计算如果Flex项目显式的设置了width值,同时显式设置了flex-basis的具体值,则Flex项目会忽略width值,会按flex-basis来计算Flex项目当Flex容器剩余空间不足时,Flex项目的实际宽度并不会按flex-basis来计算,会根据flex-grow和flex-shrink设置的值给Flex项目分配相应的空间对于Flexbox布局中,不建议显式的设置Flex项目的width值,而是通过flex-basis来控制Flex项目的宽度,这样更具弹性如果Flex项目显式的设置了min-width或max-width值时,当flex-basis计算出来的值小于min-width则按min-width值设置Flex项目宽度,反之,计算出来的值大于max-width值时,则按max-width的值设置Flex项目宽度flex-grow前面提到过,flex-grow是一个扩展因子(扩展比例)。其意思是,当Flex容器有一定的剩余空间时,flex-grow可以让Flex项目分配Flex容器剩余的空间,每个Flex项目将根据flex-grow因子扩展,从而让Flex项目布满整个Flex容器(有效利用Flex容器的剩余空间)。flex-grow的默认值是0,其接受的值是一个数值,也可以是一个小数值,但不支持负值。一旦flex-grow的值是一个大于0的值时,Flex项目就会占用Flex容器的剩余空间。在使用flex-grow时可以按下面的方式使用:所有Flex项目设置相同的flex-grow值每个Flex项目设置不同的flex-grow值不同的设置得到的效果将会不一样,但flex-grow的值始终总量为1,即Flex项目占有的量之和(分子)和分母相同。我们来具体看看flex-grow对Flex项目的影响。当所有的Flex项目具有一个相同的flex-grow值时,那么Flex项目将会平均分配Flex容器剩余的空间。在这种情况之下将flex-grow的值设置为1。比如下面这个示例,Flex容器(width: 800px,padding: 10px)中有四个子元素(Flex项目),显式的设置了flex-basis为150px,根据前面介绍的内容,我们可以知道每个Flex项目的宽度是150px,这样一来,所有Flex项目宽度总和为150px * 4 = 600px。容器的剩余空间为780px - 600px = 180px。当显式的给所有Flex项目设置了flex-grow为1(具有相同的值)。这样一来,其告诉浏览器,把Flex容器剩余的宽度(180px)平均分成了四份,即:180px / 4 = 45px。而flex-grow的特性就是按比例把Flex容器剩余空间分配给Flex项目(当然要设置了该值的Flex项目),就该例而言,就是给每个Flex项目添加了45px,也就是说,此时Flex项目的宽度从150px扩展到了195px(150px + 45px = 195px)。如下图所示:特别声明,如果Flex项目均分Flex容器剩余的空间,只要给Flex项目设置相同的flex-grow值,大于1即可。比如把flex-grow设置为10,就上例而言,把剩余空间分成了40份,每个Flex项目占10份。其最终的效果和设置为1是等效的。上面我们看到的均分Flex容器剩余空间,事实上我们也可以给不同的Flex项目设置不同的flex-grow值,这样一来就会让每个Flex项目根据自己所占的比例来占用Flex容器剩余的空间。比如上面的示例,把Flex项目的flex-grow分别设置为1:2:3:4。也就是说把Flex容器的剩余空间分成了10份(1 + 2 + 3 + 4 = 10),而每个Flex项目分别占用Flex容器剩余空间的1/10、2/10、3/10和4/10。就上例而言,Flex容器剩余空间是180px,按这样的计算可以得知,每一份的长度是180px / 10 = 18px,如此一来,每个Flex项目的宽度则变成:Flex1: 150px + 18px * 1 = 168pxFlex2: 150px + 18px * 2 = 186pxFlex3: 150px + 18px * 3 = 204pxFlex4: 150px + 18px * 4 = 222px最终效果如下图所示:前面两个示例向大家演示了,Flex项目均分和非均分Flex容器剩余的空间。从示例中可以看出来,flex-grow的值都是大于或等于1的数值。事实上,flex-grow还可以设置小数。比如,给所有Flex项目设置flex-grow的值为0.2。由于Flex项目的flex-grow的值都相等,所以扩展的值也是一样的,唯一不同的是,所有的Flex项目并没有把Flex容器剩余空间全部分完。就我们这个示例而言,四个Flex项目的flex-grow加起来的值是0.8,小于1。换句话说,四个Flex项目只分配了Flex容器剩余空度的80%,按上例的数据来计算,即是180px * .8 = 144px(只分去了144px),而且每个Flex项目分得都是36px(144px / 4 = 36px 或者 144px * 0.2 / 0.8 = 36px)。最终效果如下图所示:上面的示例中,flex-basis都显式的设置了值。事实上,flex-grow和flex-basis会相互影响的。这也令我们的Flex项目计算变得复杂化了。比如说,flex-basis的值为auto,而且没有给Flex项目显式的设置width。根据前面的内容我们可以得知,此时Flex项目的大小都取决于其内容的max-content大小。此时Flex容器的剩余的空间将由浏览器根据Flex项目的内容宽度来计算。比如接下来的这个示例,四个Flex项目都是由其内容max-content大小决定。同时将flex-grow都设置为1(均匀分配Flex容器剩余空间)。具体的数据由下图所示(Chrome浏览器计算得出的值):特别注意,不同浏览器对小数位的计算略有差异,上图是在Chrome浏览器下得出的值。所以最终加起来的值略大于Flex容器的宽度708px。针对这样的使用场景,如果你想让所有Flex项目具有相同的尺寸,那么可以显式的设置Flex项目的flex-basis值为0(flex: 1 1 0)。从flex-basis一节中可以得知,当flex-basis值为0时,表示所有空间都可以用来分配,而且flex-grow具有相同的值,因此Flex项目可以获取均匀的空间。如此一来Flex项目宽度将会相同。flex-basis还可以由其他值为设置Flex项目的宽度,这里不再一一演示。感兴趣的同学可以自己根据flex-basis的取值写测试用例。换句话说,如果你理解了前面介绍的flex-basis内容,就能更好的理解flex-grow和flex-basis相结合对Flex项目分配Flex容器剩余空间的计算。也将不会再感到困惑。flex-shrinkflex-shrink和flex-grow类似,只不过flex-shrink是用来控制Flex项目缩放因子。当所有Flex项目宽度之和大于Flex容器时,将会溢出容器(flex-wrap为nowrap时),flex-shrink就可以根据Flex项目设置的数值比例来分配Flex容器的不足空间,也就是按比例因子缩小自身的宽度,以免溢出Flex容器。flex-shrink接收一个<number>值,其默认值为1。也就是说,只要容器宽度不足够容纳所有Flex项目时,所有Flex项目默认都会收缩。如果你不想让Flex项目进行收缩时,可以设置其值为0,此时Flex项目始终会保持原始的fit-content宽度。同样的,flex-shrink也不接受一个负值做为属性值。基于上面的示例,简单的调整一下参数,所有Flex项目都设置了flex: 0 0 300px,可以看到Flex项目溢出了Flex容器:在这个示例中,由于flex-shrink显式的设置了值为0,Flex项目不会进行收缩。如果你想让Flex项目进行收缩,那么可以把flex-shrink设置为1。从上图的结果我们可以看出,当所有Flex项目的flex-shrink都设置为相同的值,比如1,将会均分Flex容器不足空间。比如此例,所有Flex项目的宽度总和是1200px(flex-basis: 300px),而Flex容器宽度是780px(width: 800px,padding: 10px,盒模型是border-box),可以算出Flex容器不足空间为420px(1200 - 780 = 420px),因为所有Flex项目的flex-shrink为1,其告诉浏览器,将Flex容器不足空间均分成四份,那么每份则是105px(420 / 4 = 105px),这个时候Flex项目就会自动缩放105px,其宽度就由当初的300px变成了195px(300 - 105 = 195px)。这个示例演示的是Flex项目设置的值都是相同的值,其最终结果是将会均分Flex容器不足空间。其实flex-shrink也可以像flex-grow一样,为不同的Flex项目设置不同的比例因子。比如1:2:3:4,这个时候Flex项目就不会均分了,而是按自己的比例进行收缩,比例因子越大,收缩的将越多。如下图所示:就上图而言,所有Flex项目的flex-shrink之和为10(1 + 2 + 3 + 4 = 10),此时把Flex容器不足空间420px分成了十份,每一份42px(420 / 10 = 42px),每个Flex项目按照自己的收缩因子相应的去收缩对应的宽度,此时每个Flex项目的宽度就变成:Flex1: 300 - 42 * 1 = 258pxFlex2: 300 - 42 * 2 = 216pxFlex3: 300 - 42 * 3 = 174pxFlex4: 300 - 42 * 4 = 132px按照该原理来计算的话,当某个Flex项目的收缩因子设置较大时,就有可能会出现小于0的现象。基于上例,如果把第四个Flex项目的flex-shrink设置为15。这样一来,四个Flex项目的收缩因子就变成:1:2:3:15。也就是说把Flex容器不足空间分成了21份,每份占据的宽度是20px(420 / 21 = 20px)。那么Flex项目的宽度就会出现0的现象(300 - 15 * 20 = 0)。这个时候会不会出现无空间容纳Flex项目的内容呢?事实上并不会这样:在Flexbox布局当中,会阻止Flex项目元素宽度缩小至0。此时Flex项目会以min-content的大小进行计算,这个大小是它们利用任何可以利用的自动断行机会后所变成的如果某个Flex项目按照收缩因子计算得出宽度趋近于0时,Flex项目将会按照该元素的min-content的大小来设置宽度,同时这个宽度将会转嫁到其他的Flex项目,再按相应的收缩因子进行收缩。比如上例,Flex项目四,其flex-shrink为15,但其宽度最终是以min-content来计算(在该例中,Chrome浏览器渲染的宽度大约是22.09px)。而这个22.09px最终按照1:2:3的比例分配给了Flex项目一至三(Flex1,Flex2和Flex3)。对应的Flex项目宽度就变成:Flex1: 300 - 20 * 1 - 22.09 / 6 * 1 = 276.334pxFlex2: 300 - 20 * 2 - 22.09 / 6 * 2 = 252.636pxFlex3: 300 - 20 * 3 - 22.09 / 6 * 3 = 228.955pxFlex4: min-content,在该例中大约是22.09px对于该情形,计算相对而言就更为复杂一些了。但浏览器会很聪明的帮你处理这些场景,会倾向于给你合理的结果。只不过大家需要知道这样的一个细节,碰到类似的场景才不会一脸蒙逼(^_^)。flex-grow可以设置一个小于0的值,同样的,flex-shrink也可以设置一个小于0的值,比如我们给所有的Flex项目设置flex-shrink的值为0.2,你将看到的结果如下:从结果的示例图中我们可以看出来,当所有Flex项目的收缩因子(flex-shrink)总和小于1时,Flex容器不足空间不会完全分配完,依旧会溢出Flex容器。好比该例,flex-shrink的总和是.8,分配了Flex容器剩余空间420px的80%,即336px(还有84px剩余空间未完全分配完),由于每个Flex项目的收缩因子是相同的,好比前面的示例,都设置了1类似,把分配的空间336px均分为四份,也就是84px,因此每个Flex项目的宽度由当初的300px变成了216px(300 - 84 = 216px)。这个其实和flex-grow类似,只不过flex-shrink只是收缩而以。Flex项目计算公式Flex项目伸缩计算是一个较为复杂的过程,但它们之间还是有据可查。@Chris和@Otree对该方面就有深入的研究。他们给Flex项目的计算总结出了一套计算公式,具体公式如下:@Chris还依据这套公式写了一个JavaScript的案例,来模拟Flex项目计算。flex常见的值大部分情形之下,我们都是使用flex属性来设置Flex项目的伸缩的值。其常见值的效果有:flex: 0 auto和flex:initial,这两个值与flex: 0 1 auto相同,也是初始值。会根据width属性决定Flex项目的尺寸。当Flex容器有剩余空间时,Flex项目无法扩展;当Flex容器有不足空间时,Flex项目收缩到其最小值min-content。flex: auto与flex: 1 1 auto相同。Flex项目会根据width来决定大小,但是完全可以扩展Flex容器剩余的空间。如果所有Flex项目均为flex: auto、flex:initial或flex: none,则Flex项目尺寸决定后,Flex容器剩余空间会被平均分给是flex:a uto的Flex项目。flex: none与flex: 0 0 auto相同。Flex项目根据width决定大小,但是完全不可伸缩,其效果和initial类似,这种情况下,即使在Flex容器空间不够而溢出的情况之下,Flex项目也不会收缩。flex: <positive-number>(正数)与flex: 1 0px相同。该值使Flex项目可伸缩,并将flex-basis值设置为0,导致Flex项目会根据设置的比例因子来计算Flex容器的剩余空间。如果所有Flex项目都使用该模式,则它们的尺寸会正比于指定的伸缩比。默认状态下,伸缩项目不会收缩至比其最小内容尺寸(最长的英文词或是固定尺寸元素的长度)更小。可以靠设置min-width属性来改变这个默认状态。如何掌握Flex项目的大小通过前面的内容介绍,应该可以了解到Flex项目的大小计算是非常的复杂。如果要真正的理解Flex项目是如何工作的话,最为关键的是理解有多少东西参与影响Flex项目。我们可以按下面这样的方式来进行思考。怎么设置Flex项目的基本大小在CSS中设置一个元素的基本大小可以通过width来设置,或者通过min-width或max-width来设置元素的最小或最大宽度,在未来我们还可以通过content、min-content、max-content或fit-content等关键词来设置元素的大小。对于Flex项目,我们还可以通过flex-basis设置Flex项目大小。对于如何设置Flex项目的基本大小,我们可以围绕以下几点来进行思考:flex-basis的值是auto?Flex项目显式的设置了宽度吗?如果设置了,Flex项目的大小将会基于设置的宽度flex-basis的值是auto还是content?如果是auto,Flex项目的大小为原始大小flex-basis的值是0的长度单位吗?如果是这样那这就是Flex项目的大小flex-basis的值是0呢? 如果是这样,则Flex项目的大小不在Flex容器空间分配计算的考虑之内更为具体的可以参阅flex-basis相关的介绍。我们有可用空间吗?如果Flex容器没有剩余空间,Flex项目就不会扩展;如果Flex容器没有不足空间,Flex项目就不会收缩:所有的Flex项目的宽度总和是否小于Flex容器的总宽度? 如果是这样,那么Flex容器有剩余空间,flex-grow会发挥作用, 具体如何发挥作用,可以参阅flex-grow相关的介绍所有的Flex项目的宽度总和是否大于Flex容器的总宽度? 如果是这样,那么Flex容器有不足空间,flex-shrink会发挥作用,具体如何发挥作用,可以参阅flex-shrink相关的介绍分配空间的其他方式如果我们不想把Flex容器的剩余空间扩展到Flex项目中,我们可以使用Flexbox中其他属性,比如justify-content属性来分配剩余空间。当然也可以给Flex项目设置margin值为处理Flex容器剩余空间。不过这一部分没有在这里阐述,如果感兴趣的话,不仿阅读一下Flexbox相关的介绍。总结很久以为,一直以为Flexbox布局中,Flex项目都会根据Flex容器自动计算。而事实上呢?正如文章中介绍的一样,Flex项目的计算是相当的复杂。设置Flex项目大小的值以及flex-basis、flex-grow和flex-shrink的设置都会对其有较大的影响,而且它们的组合场景也是非常的多,并且不同的场景会造成不一样的结果。当然,文章中所介绍的内容或许没有覆盖到所有的场景,但这些基本的演示或许能帮助大家更好的理解Flex项目是如何计算的。最后希望该文对大家有所帮助,如果你有更深的了解欢迎在下面的评论中与我一起分享。如果文章中有不对之处,还望各路大婶拍正。本文作者:大漠_w3cplus阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

November 23, 2018 · 2 min · jiezi

蚂蚁金融科技全面开放战略背后的技术布局

导读:蚂蚁金融科技全面开放战略的公布,意味着蚂蚁金融科技正式进入全新的3.0时代。蚂蚁金融科技15年来的演进,在其发展史上不断留下了技术里程碑,同时,也缔造出一个又一个的业界“奇迹”。本文将深度揭秘蚂蚁金服的技术战略及布局。11月7日,第五届世界互联网大会在浙江乌镇开幕。当天下午,代表着行业最高水准的“世界互联网领先科技成果”发布,蚂蚁金服自主可控的金融级商用区块链平台等十余项先进技术获得此项殊荣。11月8日,世界互联网大会上由中国人民银行科技司主办的“金融科技与信用社会建设”主题论坛,蚂蚁金服宣布基于网络黑灰产防控治理的“天朗计划”全面升级,将蚂蚁风险大脑对传销、非法集资、金融诈骗等金融风险防控融入其中,同时,蚂蚁风险大脑也将会全面赋能合作伙伴,为其业务发展保驾护航,为投资者和消费者提供一个天朗气清的网络空间。而在同期举行的 “互联网之光”博览会上,蚂蚁金服不仅展示了15年来的技术演进之路,而且重点展示了区块链技术和蚂蚁风险大脑等相关产品及解决方案。蚂蚁金服15年技术演进之路经过15年的发展,蚂蚁金服已经成长为中国最重要的金融科技平台。除了支付、信贷、保险、理财、征信等众多业务,在金融技术的研发、投入及开放上,也已深耕不辍了多年。最初的蚂蚁金融云逐渐演变,在积累了众多云计算能力和技术组件的基础上,早已超越了传统金融云的概念,而囊括了云计算、大数据、AI、IoT、区块链等一整套技术体系。我们势必需要重新认识蚂蚁金服,将它的立体与多维展现出来,让金融科技的价值被真正重视!全面开放:数字金融技术整体解决方案当下,以支付宝为代表的蚂蚁金服的产品从一开始就要接受来自于网上的各种检验、钓鱼、攻击、窃取。面对一个开放的系统,没有办法关起门来,需要去研发各种技术,这对技术能力是非常大的考验。3年前,蚂蚁金服启动了“互联网推进器”计划,该计划表明,蚂蚁金服将在5年内助力超过1000家金融机构向新金融转型升级,在平台、数据和技术等方面实施能力全面对外开放。从2015年蚂蚁金融云发布到2016年GeaBase在支付场景上线,从2017年OceanBase 三地五中心集群部署到2018年“蚂蚁风险大脑”上线,蚂蚁金融科技开放一直在往纵深方向发展。到了今天,蚂蚁金服已经逐渐形成了“点线面”相结合的技术解决方案体系,包含了海量金融交易技术、金融智能技术、新一代金融交互技术、金融安全、区块链、综合技术等。在蚂蚁金融科技的大版图上,强调“技术开放”,而不是“技术输出”,或许这不仅仅是喊的口号不同,更是因为两者所导致的结果是真正差异化的:“开放”更具开源精神,它助推着金融科技的融合与创新,让新金融变成了“更好的金融”。在此前云栖ATEC主论坛上,蚂蚁金服副CTO胡喜就宣布,蚂蚁金服的金融科技正式全面开放,为行业提供完整的数字金融解决方案。包括容灾系统在内的多项核心技术和解决方案,如金融安全、蚂蚁风险大脑、区块链等都将对合作伙伴开放。五大关键技术详解:蚂蚁金服的“完整答卷”除了让金融机构摆脱传统IT架构的束缚外,蚂蚁金服也成就了自己世界级的技术能力。在多条重要技术主线上,无数的金融场景都能被一一对应,蚂蚁金服也由此不断进入“新领地”,在技术前沿展开探索。因此,主要梳理的技术主线有如下5条:海量金融交易技术交易技术是支付宝创造“人间奇迹”的起点。2017年的“双11”,支付宝凭借多项新纪录成为当天的主角:11秒钟破亿,28秒钟破10亿,3分01秒破100亿,6分05秒钟破200亿。根据支付宝官方数据,第5分22秒,双11的支付峰值达到25.6万笔/秒,同时蚂蚁金服自主研发的数据库处理峰值达到4200万次/秒,双双创下新纪录。而海量金融交易技术的背后,其实是分布式架构所带来的创新优势,敏捷迭代、容灾安全、弹性伸缩构成了分布式架构转型升级的三大驱动力。其中,OceanBase分布式数据库、SOFA ware分布式中间件、CAF?容器云平台三大技术构成了蚂蚁金服金融级的分布式架构。金融交易技术最关键的目标是“数据不丢失,业务不停机”。目前,TRaaS 技术风险防控平台(Technological Risk-defence as a Service)已一跃成为技术风险领域最为成熟的产品,在高可用架构(异地多活、全链路压测)、资损防控(交易实时核对、自动决策)、智能运维(AIOps故障自愈)等功能上做到了极致。而金融级分布式架构SOFAStack(Scalable Open Financial Architecture Stack)专注为金融用户提供安全、敏捷的基础架构能力,解决传统集中式架构转型的困难,将传统集中式架构转变为分布式系统架构。以人保健康为例,借助SOFAStack,其互联网保险云核心业务处理能力提升了上千倍,并支持弹性扩容,出单时间达到每秒1000单,外部渠道产品接入效率提升6倍,新产品上线时间缩短80%以上。经过四代架构演进,八年“双十一”的考验,现在SOFA已经从中间件这一层开始,逐渐对外进行开放和开源。金融安全技术安全是蚂蚁金服BASIC五大技术开放战略之一(Blockchain区块链、Artificial intelligence金融智能、Security安全、IoT物联网和Computing计算),事实上十多年以来,在业务场景的迫切需求的驱动下,蚂蚁金服的风控技术也经历了多次升级迭代,才发展成一套以AI智能算法、生物核身为基础的多层级立体闭环风控系统——蚂蚁风险大脑(Risk Brian)。人脸识别、指纹识别、虹膜识别、活体检测等技术加上智能设备终端、传感器识别等共同组成了蚂蚁风险大脑数字核身解决方案。面对日益复杂的新金融场景监管,地方金融监管机构正在承担越来越多的责任,同时监管痛点也逐步显现出来。而当下,蚂蚁风险大脑集风险感知、风险识别、智能进化、自动策略调整4大功能为一身,已形成了“金融消费者教育预警”、“7+4行业监测”、“涉众金融风险防控”、“金融风险联动处置”、“投资人信访登记”等完整的防控链路。目前,蚂蚁风险大脑已与北京、天津、广州等多个地方金融监管部门建立合作,共同保护消费者合法权益。全面、多样化的数字身份认证体系也是安全技术一大亮点。运用ZOLOZ(生物认证),对人脸识别的准确率高达99.99%, 覆盖2亿+互联网金融用户,确保20亿+次交易安全;运用IFAA(本地生物认证框架),实现覆盖终端设备12亿台,支持380款Android手机,TEE级别、高安全性,设备接入轻量快速低成本,周期从4个月降至1周内。除此之外,AlphaRisk (智能风控引擎)在风险感知、风险识别、智能进化、自动驾驶上也有着诸多应用,向着自动化、自学习、高准确率、高计算性能、自适应的方向进化。金融智能决策技术蚂蚁金服的金融智能决策技术与旗下网商银行独创的“310”模式息息相关。“310”即“3分钟申请、1秒钟到账、0人工干预”的服务标准,至今服务了1000万中小微企业的贷款。2018年6月,蚂蚁金服董事长兼CEO井贤栋透露,网商银行将启动“凡星计划”:未来三年,网商银行将与1000家各类金融机构合作,服务3000万家小微企业和个体经营户。在过去,发放一笔小微企业贷款的平均人力成本在2000元,而网商银行通过技术支撑的“310”模式,让每笔贷款的平均运营成本降低至2.3元,其中2元是计算和存储硬件等技术投入费用。可以说,技术降低了金融服务的成本,实现了商业上的可持续发展。具体运行方式上,金融智能决策技术涉4大步骤,分别是数据的采集与计算、AB试验体系和BI深度分析、统一指标与策略管理、模拟训练和预测平台等,至今已积累10万余项指标体系、3000多种风控策略,仅行业化风控模型就建立100多个。与此同时,在2017双11支付宝 25.6万笔支付每秒也需要其迅速做出大量高效的决策,最难得的是其资损率一直小于百万分之0.5的水平,交易资金的安全性得到了高度保障。新一代金融交互技术在新一代金融交互技术上,蚂蚁金服有着坚实的中台能力,新一代mPaaS平台,让客户端运行更为高效与稳定;小程序的组件和API则将支付宝特色能力与系统原生能力做了紧密地结合,实现了一次开发多端投放,并可以无缝迁移支付宝小程序到自己的App中;Ant Design让用户研究、交互模式、设计语言等形成了良好的循环圈,将终端用户的体验提升到了极致。具体来看,开发者能够利用蚂蚁金服移动开发平台mPaaS做好移动App的开发、管理、发布,并做好App全生命周期的管理,其中包括了开发期的研发测试、打包构建、发布管理,还有发布之后的用户行为分析、闪退分析等。2018年9月27日,支付宝小程序一站式云服务正式开放公测,为小程序开发者提供了完整的云端支持,让开发者无需自己搭建服务器,即可实现支付宝小程序的快速上线和迭代,大大节省开发成本、加快开发速度。如果说PaaS平台是对企业后台服务的生命周期的管理,包括研发、发布、监控这一套流程,那么mPaaS就是对移动应用App一整套全生命周期的管理服务,能有效降低技术门槛、减少研发成本、提升开发效率,协助金融机构快速搭建稳定高质量的移动应用。区块链技术蚂蚁金服自研可控的金融级区块链平台已经在多个社会和商业应用场景实现多机构、多国全球部署,提供面向政府、企业和普通百姓的各类数字服务。同时,蚂蚁区块链解决了很多区块链产业面临的技术挑战,在性能、安全性以及跨链交互等多个技术难点的研究与攻关进展方面均处于世界前列,已经具备金融级平台所需要的高性能、高可靠和高安全的技术特点。蚂蚁金服区块链总体定位是做一个信任连接的基础设施,与信美人寿相互保险社的合作是其区块链技术的试水,而在天猫跨境电商溯源、茅台溯源这两个可信的溯源服务上,区块链技术日臻成熟。2017年11月,蚂蚁金服正式上线关于天猫境外商品的跨境溯源的服务。在这个场景中,支付宝用户从天猫针对来自澳洲、新西兰26个商品、奶制品提供了关于每一瓶奶制品的身份证的溯源码服务。目前,蚂蚁金服在区块链领域申请了160多件专利,国家专利局公开授权的有65件,在知识产权产业媒体IPRdaily最新发布的《2018年全球区块链专利企业排行榜》排名第一。目前,蚂蚁区块链的专利方向主要集中于底层技术,并将这些技术首先应用在公益慈善、食品安全、跨境汇款、房屋租赁等更具社会价值的民生领域。2018年6月25日,蚂蚁金服宣布全球首个基于区块链技术的电子钱包跨境汇款服务在香港上线。在香港工作22年的菲律宾人Grace幸运地第一个尝鲜,整个汇款过程耗时仅3秒,而在以前需要短则10分钟、长则几天。在区块链技术的支持下,支付宝香港钱包可以为在港菲律宾劳工提供7×24小时不间断的跨境汇款服务,实现3-6秒汇款到账服务体验。同时,大大降低了多方对账成本。据了解,蚂蚁金服依次攻克了符合各国监管要求的隐私保护设计、区块链节点跨境多地多机房部署、低延时智能合约交易确认等技术难题。“BASIC”综合技术实力跻身一线可以说,除了上文介绍的数字金融五大关键技术,蚂蚁金服的综合技术实力在经历了“原始积累”之后,已经跻身一线技术厂商。“BASIC”是现在的蚂蚁金服最常提到的核心技术能力,即Blockchain(区块链)、AI(金融智能)、Security(安全)、IoT(物联网)、Computing(计算),未来所有的金融科技都围绕着这些技术来展开并实施开放开源。此外,蚂蚁金服完全自主研发的OceanBase 是高性能、高可扩展、数据强一致的金融级分布式关系型数据库,具备丰富的关系数据库功能,支持完整的 ACID 特性,首创的“三地五中心”城市级故障无损容灾方案正成为越来越多金融机构核心系统高可用的选择。OceanBase还高度兼容 MySQL,让用户能够以最小的迁移成本使用高性能、可扩展、持续可用的分布式数据库服务,同时对用户数据提供金融级可靠性的保障。而在最近几年被业界广泛关注的图数据库领域,蚂蚁金服经过3年多的探索,也自主研发出多项指标领先业界的金融级分布式图数据库GeaBase。GeaBase具备高性能、高可用、高扩展性及可移植性强等多重特性,支撑着蚂蚁金服旗下支付的风险控制、反洗钱、反欺诈、反刷单、反套现、金融案件审理、知识图谱、会员拉新、好友推荐、理财资讯推荐等众多的业务和应用。目前,GeaBase不仅广泛应用于蚂蚁金服的生态体系内,而且已经技术对外开放,正与多家银行等企业开展合作。从开放到开源,蚂蚁金服的“暖科技”在探索新的空间蚂蚁金服是最早提出技术开放的企业之一。在2015-2018三年间,从“互联网推进器计划”到“成熟一个开放一个”,蚂蚁金服公布的产品数量从5个增长到了80个,解决方案从3个发展到了50个,未来将以“蚂蚁金融科技”为技术输出品牌。现在,蚂蚁金融科技正式进入了3.0时代:支付宝对内延续“BASIC”战略,对外开放的技术越来越完整、越来越核心,是成建制、有体系的全面开放,并实现了技术商业化。业务上,实现了余额宝开放、借呗开放、花呗开放、小微企业贷款开放、蚂蚁财富平台、蚂蚁保险平台、蚂蚁森林等的开放……能力上,实现了小程序、生活号、实名核身能力、信用能力、风控能力、会员运营能力的开放……技术上,实现了区块链、金融智能、金融安全、金融分布式框架、移动开发、金融分布式数据库等100%开放……此外,在开源这条路上,蚂蚁金服一直保持着自己的节奏。2016年5月,企业级产品的设计体系Ant Design 1.0正式发布;2016年9月,企业级Node.js框架Egg宣布开源;2017年11月,专业的数据可视化解决方案AntV 3.0发布;2018年1月,首届蚂蚁金服体验科技大会在杭州蚂蚁Z空间成功举办;2018年4月,Ant Design成为国内公司star数最多的开源项目;2018年4月,蚂蚁金服启动分布式中间件开源计划,用于快速构建金融级云原生架构。……蚂蚁金服副CTO胡喜此前表示,蚂蚁金服内部建立了一个开源共建的体制,代码完全开放,其他业务的垂直BU自己也可以实现定制化需求。蚂蚁金服CTO程立曾说过,蚂蚁金服不是为了做技术本身而做技术,而希望用技术来解决社会当下和未来的问题。如果说用金字塔结构来描绘数字金融的社会价值,在塔顶的就是数字金融能在全球范围内带来更多平等的机会。同样,面向未来的技术探索与挑战,对“数据不丢失,业务不停机”保持极致追求,让整个数字世界变得安全可信,给全世界每一个人可信数字身份,在IoT的海量数据之上实时安全计算,蚂蚁金服责无旁贷。本文作者:华蒙阅读原文本文为云栖社区原创内容,未经允许不得转载。

November 13, 2018 · 1 min · jiezi

iOS自动布局——Masonry详解

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~本文由鹅厂新鲜事儿发表于云+社区专栏作者:oceanlong | 腾讯 移动客户端开发工程师前言UI布局是整个前端体系里不可或缺的一环。代码的布局是设计语言与用户视觉感受沟通的桥梁,不论它看起来多么简单或是琐碎,但不得不承认,绝大部分软件开发的问题,都是界面问题。那么,如何高效的完成UI开发,也是软件行业一直在克服的问题。所以,软件界面开发的核心点即是:如何减少UI设计稿的建模难度和减少建模转化到代码的实现难度最初iOS提供了平面直角坐标系的方式,来解决布局问题,即所谓的手动布局。平面直角坐标系确实是一套完备在理论,这在数学上已经验证过了,只要我们的屏幕还是平面,它就肯定是有效的。但有效不一定高效,我们在日常的生活中,很少会用平面直角坐标系来向人描述位置关系。更多的是依靠相对位置。所幸,iOS为我们提供自动布局的方法,来解决这一困境。自动布局的基本理念其实说到本质,它和手动布局是一样的。对一个控件放在哪里,我们依然只关心它的(x, y, width, height)。但手动布局的方式是,一次性计算出这四个值,然后设置进去,完成布局。但当父控件或屏幕发生变化时,子控件的计算就要重新来过,非常麻烦。因此,在自动布局中,我们不再关心(x, y, width, height)的具体值,我们只关心(x, y, width, height)四个量对应的约束。约束那么何为约束呢?obj1.property1 =(obj2.property2 * multiplier)+ constant value子控件的某一个量一定与另一个控件的某一个量呈线性关系,这就是约束。那么,给(x, y, width, height)四个量,分别给一个约束,就可以确定一个控件的最终位置。 //创建左边约束 NSLayoutConstraint *leftLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:20]; [self.view addConstraint:leftLc];这一段代码即是:控件(blueView)的 x = rootView的x * 1.0 + 20这里一定要注意,这样的一条约束,涉及了子控件和父控件,所以这条约束一定要添加到父控件中。添加约束的规则:如果两个控件是父子控件,则添加到父控件中。如果两个控件不是父子控件,则添加到层级最近的共同父控件中。示例 //关闭Autoresizing blueView.translatesAutoresizingMaskIntoConstraints = NO; //创建左边约束 NSLayoutConstraint *leftLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:20]; [self.view addConstraint:leftLc]; //创建右边约束 NSLayoutConstraint *rightLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-20]; [self.view addConstraint:rightLc]; //创建底部约束 NSLayoutConstraint *bottomLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-20]; [self.view addConstraint:bottomLc]; //创建高度约束 NSLayoutConstraint *heightLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:50]; [blueView addConstraint: heightLc];我们注意到,自动布局其实工作分两步:创建视图的约束将约束添加到合适的位置约束关系从上面的描述中,已经非常清晰了。那么如何寻找约束添加的合适位置呢?到这里,我们只是解决了如何减少UI设计稿的建模难度的问题,显然,减少建模转化到代码的实现难度这个效果没能达成。关于如何解决减少建模转化到代码的实现难度的问题,开源库上面的代码,我们可以看到,虽然自动布局已经比手动布局优雅不少了,但它依然行数较多。每条约束大约都需要三行代码,面对复杂的页面,这样开发出来,会很难阅读。Masonry则为我们解决了这个问题。Masonry地址引入Masonry我们选择使用Cocoapods的方式。引入比较简单:我们先在工程目录下,创建Podfile文件:2.编辑Podfile其中,‘IosOcDemo’就是我们工程的名字,根据需要,我们自行替换。3.添加依赖完成后,执行指令pod install。CocoaPods就会为我们自动下载并添加依赖。实践这样的一个代码,用手动布局,我们大致的代码应该是这样:-(void)initBottomView{ self.bottomBarView = [[UIView alloc]initWithFrame:CGRectZero]; self.bottomButtons = [[NSMutableArray alloc]init]; _bottomBarView.backgroundColor = [UIColor yellowColor]; [self addSubview:_bottomBarView]; for(int i = 0 ; i < 3 ; i++) { UIButton *button = [[UIButton alloc]initWithFrame:CGRectZero]; button.backgroundColor = [UIColor redColor]; [_bottomButtons addObject:button]; [self addSubview:button]; }}-(void)layoutBottomView{ _bottomBarView.frame = CGRectMake(20, _viewHeight - 200, _viewWidth - 40, 200); for (int i = 0 ; i < 3; i++) { UIButton button = _bottomButtons[i]; CGFloat x = i * (_viewWidth - 40 - 20 * 4) / 3 + 20(i+1) + 20; CGFloat y = _viewHeight - 200; CGFloat width = (_viewWidth - 40 - 20 * 4) / 3; CGFloat height = 200; button.frame = CGRectMake(x, y, width, height); }}我们来看一下,在Masonry的帮助下,我们可以把刚刚的代码写成什么样的: -(void)initBottomView{ _bottomBarView = [[UIView alloc]initWithFrame:CGRectZero]; _bottomBarView.backgroundColor = [UIColor yellowColor]; _bottomBarView.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:_bottomBarView]; [_bottomBarView mas_makeConstraints:^(MASConstraintMaker make) { make.left.equalTo(self).with.offset(20); make.right.equalTo(self).with.offset(-20); make.height.mas_equalTo(200); make.bottom.equalTo(self); }]; _bottomButtons = [[NSMutableArray alloc]init]; for(int i = 0 ; i < 3 ; i++) { UIButton button = [[UIButton alloc]initWithFrame: CGRectZero]; button.backgroundColor = [UIColor redColor]; button.translatesAutoresizingMaskIntoConstraints = NO; [_bottomButtons addObject:button]; [_bottomBarView addSubview:button]; [button mas_makeConstraints:^(MASConstraintMaker make) { if (i == 0) { make.left.mas_equalTo(20); }else{ UIButton previousButton = _bottomButtons[i-1]; make.left.equalTo(previousButton.mas_right).with.offset(20); } make.top.mas_equalTo(_bottomBarView.mas_top); make.width.equalTo(_bottomBarView.mas_width).with.multipliedBy(1.0f/3).offset(-204/3); make.height.equalTo(_bottomBarView.mas_height); }]; }}我们可以看到在Masonry的封装下,代码变得非常简练易读,需要行数略有增加,但是计算过程减少了,我们能更加关注于多个UIView间的位置关系,这与当前的UI设计语言是契合的。所以Masonry能否让我们更直观地表达UI。源码解读Masonry的封装很有魅力,那么,我们可以简单地来看一下,它是如何封装的。我们再仔细看一下Masonry的API会发现,我们是直接在UIView上进行调用的。也就是说,Masonry对UIView进行了扩展。在View+MASUtilities.h中:#if TARGET_OS_IPHONE || TARGET_OS_TV #import <UIKit/UIKit.h> #define MAS_VIEW UIView #define MAS_VIEW_CONTROLLER UIViewController #define MASEdgeInsets UIEdgeInsets然后在View+MASAdditions.h中,我们看到了Masonry的扩展:#import “MASUtilities.h”#import “MASConstraintMaker.h”#import “MASViewAttribute.h”/ * Provides constraint maker block * and convience methods for creating MASViewAttribute which are view + NSLayoutAttribute pairs /@interface MAS_VIEW (MASAdditions)/ * following properties return a new MASViewAttribute with current view and appropriate NSLayoutAttribute */@property (nonatomic, strong, readonly) MASViewAttribute *mas_left;@property (nonatomic, strong, readonly) MASViewAttribute *mas_top;@property (nonatomic, strong, readonly) MASViewAttribute *mas_right;@property (nonatomic, strong, readonly) MASViewAttribute *mas_bottom;@property (nonatomic, strong, readonly) MASViewAttribute *mas_leading;@property (nonatomic, strong, readonly) MASViewAttribute *mas_trailing;@property (nonatomic, strong, readonly) MASViewAttribute *mas_width;@property (nonatomic, strong, readonly) MASViewAttribute *mas_height;@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerX;@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerY;@property (nonatomic, strong, readonly) MASViewAttribute mas_baseline;@property (nonatomic, strong, readonly) MASViewAttribute (^mas_attribute)(NSLayoutAttribute attr);…/ * Creates a MASConstraintMaker with the callee view. * Any constraints defined are added to the view or the appropriate superview once the block has finished executing * * @param block scope within which you can build up the constraints which you wish to apply to the view. * * @return Array of created MASConstraints */- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;一些,适配的代码,我省略了,先看核心代码。在刚刚的例子中,我们正是调用的mas_makeConstraints方法。- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { self.translatesAutoresizingMaskIntoConstraints = NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; block(constraintMaker); return [constraintMaker install];}mas_makeConstraints方法比较简单,只是封装了MASConstraintMaker初始化,设置约束和安装。这里的block就是我们刚刚在外层设置的约束的函数指针。也就是这一串:^(MASConstraintMaker *make) { make.left.equalTo(self.view).with.offset(10); make.right.equalTo(self.view).with.offset(-10); make.height.mas_equalTo(50); make.bottom.equalTo(self.view).with.offset(-10); }由于约束条件的设置比较复杂,我们先来看看初始化和安装。初始化- (id)initWithView:(MAS_VIEW *)view { self = [super init]; if (!self) return nil; self.view = view; self.constraints = NSMutableArray.new; return self;}初始化的代码比较简单,将传入的view放入MASConstraintMaker成员,然后创建MASConstraintMaker的约束容器(NSMutableArray)。安装- (NSArray *)install { if (self.removeExisting) { NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view]; for (MASConstraint *constraint in installedConstraints) { [constraint uninstall]; } } NSArray *constraints = self.constraints.copy; for (MASConstraint *constraint in constraints) { constraint.updateExisting = self.updateExisting; [constraint install]; } [self.constraints removeAllObjects]; return constraints;}安装的代码分为三块:判断是否需要移除已有的约束。如果需要,会遍历已有约束,然后逐个uninstallcopy已有的约束,遍历,并逐一installremove掉所有约束,并将已添加的constraints返回。install的方法,还是继续封装到了Constraint中,我们继续跟进阅读:我们会发现Constraint只是一个接口,Masonry中对于Constraint接口有两个实现,分别是:MASViewConstraint和MASCompositeConstraint。这两个类,分别是单个约束和约束集合。在上面的例子中,我们只是对单个UIView进行约束,所以我们先看MASViewConstraint的代码。以下代码MASViewConstraint进行了一定程度的简化,省略了一些扩展属性,只展示我们的例子中,会执行的代码:- (void)install { if (self.hasBeenInstalled) { return; } … MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item; NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute; MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item; NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute; // alignment attributes must have a secondViewAttribute // therefore we assume that is refering to superview // eg make.left.equalTo(@10) if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) { secondLayoutItem = self.firstViewAttribute.view.superview; secondLayoutAttribute = firstLayoutAttribute; } MASLayoutConstraint *layoutConstraint = [MASLayoutConstraint constraintWithItem:firstLayoutItem attribute:firstLayoutAttribute relatedBy:self.layoutRelation toItem:secondLayoutItem attribute:secondLayoutAttribute multiplier:self.layoutMultiplier constant:self.layoutConstant]; layoutConstraint.priority = self.layoutPriority; layoutConstraint.mas_key = self.mas_key; if (self.secondViewAttribute.view) { MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view]; NSAssert(closestCommonSuperview, @“couldn’t find a common superview for %@ and %@”, self.firstViewAttribute.view, self.secondViewAttribute.view); self.installedView = closestCommonSuperview; } else if (self.firstViewAttribute.isSizeAttribute) { self.installedView = self.firstViewAttribute.view; } else { self.installedView = self.firstViewAttribute.view.superview; } MASLayoutConstraint *existingConstraint = nil; … else { [self.installedView addConstraint:layoutConstraint]; self.layoutConstraint = layoutConstraint; [firstLayoutItem.mas_installedConstraints addObject:self]; }}自动布局是一种相对布局,所以,绝大部分情况下,需要两个UIView(约束方与参照方)。在上面的方法中:firstLayoutItem是约束方,secondLayoutItem是参照方firstLayoutAttribute是约束方的属性,secondLayoutAttribute是参照方的属性。MASLayoutConstraint就是NSLayoutConstraint的子类,只是添加了mas_key属性。到这里,我们就与系统提供的API对应上了。 NSLayoutConstraint *leftLc = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:20]; [self.view addConstraint:leftLc];再看看我们之前用系统API完成的例子,是不是格外熟悉?那么接下来,我们就是要阅读 make.left.equalTo(self).with.offset(20); make.right.equalTo(self).with.offset(-20); make.height.mas_equalTo(200); make.bottom.equalTo(self);是如何变成firstLayoutItem, secondLayoutItem, firstLayoutAttribute, secondLayoutAttribute和layoutRelation的。约束条件的设置回到前面的:- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { self.translatesAutoresizingMaskIntoConstraints = NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; block(constraintMaker); return [constraintMaker install];}我们接下来,就要看block的实现:block其实是一个函数指针。此处真正调用的方法是: make.left.equalTo(self).with.offset(20); make.right.equalTo(self).with.offset(-20); make.height.mas_equalTo(200); make.bottom.equalTo(self);我们挑选其中一个,来看看源码实现:left- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];}- (MASConstraint *)left { return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];}- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute]; MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute]; if ([constraint isKindOfClass:MASViewConstraint.class]) { //replace with composite constraint NSArray *children = @[constraint, newConstraint]; MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children]; compositeConstraint.delegate = self; [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint]; return compositeConstraint; } if (!constraint) { newConstraint.delegate = self; [self.constraints addObject:newConstraint]; } return newConstraint;}在对单个view添加约束时,constraint为nil。我们直接生成了一个新约束newConstraint。它的firstViewAttribute就是我们传入的NSLayoutAttributeLeftequalTo- (MASConstraint * (^)(id))equalTo { return ^id(id attribute) { return self.equalToWithRelation(attribute, NSLayoutRelationEqual); };}- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { return ^id(id attribute, NSLayoutRelation relation) { if ([attribute isKindOfClass:NSArray.class]) { NSAssert(!self.hasLayoutRelation, @“Redefinition of constraint relation”); NSMutableArray *children = NSMutableArray.new; for (id attr in attribute) { MASViewConstraint *viewConstraint = [self copy]; viewConstraint.layoutRelation = relation; viewConstraint.secondViewAttribute = attr; [children addObject:viewConstraint]; } MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children]; compositeConstraint.delegate = self.delegate; [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint]; return compositeConstraint; } else { NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @“Redefinition of constraint relation”); self.layoutRelation = relation; self.secondViewAttribute = attribute; return self; } };}此处,我们依然先看attribute不是NSArray的情况。这里在单个属性的约束中,就比较简单了,将relation和attribue传入MASConstraint对应的成员。在上面介绍install方法时,我们就曾提到过: MASLayoutConstraint *layoutConstraint = [MASLayoutConstraint constraintWithItem:firstLayoutItem attribute:firstLayoutAttribute relatedBy:self.layoutRelation toItem:secondLayoutItem attribute:secondLayoutAttribute multiplier:self.layoutMultiplier constant:self.layoutConstant];firstLayoutItem和secondLayoutItem在install方法中已收集完成,此时,经过left和equalTo我们又收集到了:firstViewAttribute、secondViewAttribute和layoutRelation胜利即在眼前。- (MASConstraint * (^)(CGFloat))offset { return ^id(CGFloat offset){ self.offset = offset; return self; };}- (void)setOffset:(CGFloat)offset { self.layoutConstant = offset;}通过OC的set语法,Masonry将offset传入layoutConstant。至此,layoutConstraint就完成了全部的元素收集,可以使用添加约束的方式,只需要解决最后一个问题,约束添加到哪里呢?我们似乎在调用时,并不需要关心这件事情,那说明框架帮我们完成了这个工作。closestCommonSuperview我们在MASViewConstraint中,可以找到这样一段: if (self.secondViewAttribute.view) { MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view]; NSAssert(closestCommonSuperview, @“couldn’t find a common superview for %@ and %@”, self.firstViewAttribute.view, self.secondViewAttribute.view); self.installedView = closestCommonSuperview; } else if (self.firstViewAttribute.isSizeAttribute) { self.installedView = self.firstViewAttribute.view; } else { self.installedView = self.firstViewAttribute.view.superview; }注意到,closetCommonSuperview就是Masonry为我们找到的最近公共父控件。- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view { MAS_VIEW *closestCommonSuperview = nil; MAS_VIEW *secondViewSuperview = view; while (!closestCommonSuperview && secondViewSuperview) { MAS_VIEW *firstViewSuperview = self; while (!closestCommonSuperview && firstViewSuperview) { if (secondViewSuperview == firstViewSuperview) { closestCommonSuperview = secondViewSuperview; } firstViewSuperview = firstViewSuperview.superview; } secondViewSuperview = secondViewSuperview.superview; } return closestCommonSuperview;}实现也比较简单。至此,我们完成了所有准备,就可以开始愉快的自动布局啦。以上就是Masonry对iOS自动布局封装的解读。如有问题,欢迎指正。问答iOS:如何使用自动布局约束?相关阅读走进 MasonryiOS自动布局框架之MasonryiOS学习——布局利器Masonry框架源码深度剖析 【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识 ...

October 30, 2018 · 5 min · jiezi