关于ui:在TMP中计算书名号高度的问题

<article class=“article fmt article-content”><p>1)在TMP中计算书名号《》高度的问题<br/>2)FMOD设置中对于Virtual Channel Count&Real Channel Count的参数疑难<br/>3)Unity 2021.3.18f1 ParticleSystemTrailGeometryJob粒子拖尾零碎解体<br/>4)XLua打包Lua文件粒度问题</p><hr/><p>这是第375篇UWA技术常识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区帖子等技术知识点,助力大家更全面地把握和学习。</p><p>UWA社区主页:community.uwa4d.com<br/>UWA QQ群:465082844</p><h2>UI</h2><p><strong>Q:如下图所示,输出的文字中有书名号,但应用ContentSIzeFitter计算失去的高度是谬误的(貌似它计算的比理论少,导致没换行):</strong></p><p></p><p></p><p></p><p><strong>而应用默认的Text就没这个问题(雷同的字体):</strong></p><p></p><p><strong>请问如何在TMP中计算书名号《》的高度?</strong></p><blockquote><p>A:如同是对东亚语言有非凡解决,都会换行。不确定是不是这儿的起因,输出“你好”两字也是一样的成果,能够在这儿改改试试。</p><p></p><p></p><p>把这行正文掉就没问题了,但我不保障没其余问题,因为要把TMP的包导入我的项目里,源码会少算了换行第一个字符的长度,GeneratedMesh和PreferedHeight是离开算的,所以显示和高度计算不一样,GeneratedMesh是对的,这是Unity本人的Bug,前面几个版本修复了,预计还是得想方法降级TMP的版本。</p><p>参考文档:<br/>https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2/c…</p></blockquote><p><strong>感激萌呆瞎@UWA问答社区提供了答复,欢送大家转至社区交换:</strong></p><h2>Audio</h2><p><strong>Q:FMOD的设置中,我发现有两个设置选项:Virtual Channel Count和Real Channel Count。想求教一下,个别游戏我的项目内这两个值要设置多少才适合?我发现外网有人都是拉满的,但这会造成CPU累赘。只晓得Real Channel Count这个不能太高。所以想理解下通常这俩参数设置多少比拟正当。</strong></p><p><strong>下图来自外网论坛,Virtual Channel Count设置为512、Real Channel Count设置为128,而我的我的项目中这俩设置为128、32。</strong></p><p></p><blockquote><p>A:Real Channel,如果设置成32对于PC来说是正好。对于挪动平台,32应该也能跑。对于超古老的机器,能够思考设置成16。</p><p>Real Channel是同时播放的音频Channel数,如果同时播放的Channel超过这个数字,就会依据优先级把低优先级的音频停掉。理论我的项目中,极少同时播放如此之多的Channel。如果真产生了,请认真优化音频播放逻辑。</p></blockquote><p><strong>感激Zhang Ce@UWA问答社区提供了答复</strong></p><h2>Crash</h2><p><strong>Q:Unity 2021.3.18f1 ParticleSystemTrailGeometryJob粒子拖尾零碎解体。</strong></p><p><strong>信息如下:</strong><br/><strong>解体手机:Galaxy S24 Ultra</strong></p><p><strong>操作系统:Android14</strong></p><p><strong>解体堆栈:</strong></p><p><strong>Crashed: Thread: SIGSEGV 0x0000007d2f34a900</strong><br/><strong>#00 pc 0xb30240 libunity.so (ParticleSystemTrailGeometryJob::ConfigurePerParticleTrailParams(ParticleLineParameters&, ParticleSystemParticles const*, unsigned long, ParticleSystemTrailGeometryJob const&, float)) (BuildId: c89a8ec16cd55b42b7a6c100718f0e187c531fc3)</strong><br/><strong>#01 pc 0xb29b08 libunity.so (ParticleSystemTrailGeometryJob::ConfigurePerParticleTrailParams(ParticleLineParameters&, ParticleSystemParticles const*, unsigned long, ParticleSystemTrailGeometryJob const&, float)) (BuildId: c89a8ec16cd55b42b7a6c100718f0e187c531fc3)</strong><br/><strong>#02 pc 0xb136b8 libunity.so (ParticleSystemGeometryJob::RenderJobCommon(ParticleSystemGeometryJob&, void*, void*)) (BuildId: c89a8ec16cd55b42b7a6c100718f0e187c531fc3)</strong><br/><strong>#03 pc 0x5d084 libc.so (BuildId: 37f537c2ba9dcbb262a0a68f41a21da4)</strong></p><p></p><p><strong>针对以上问题,有教训的敌人欢送转至社区交换分享:</strong><br/>https://answer.uwa4d.com/question/65d87ea040a8d93b624afce9</p><hr/><h2>Lua</h2><p><strong>Q:目前我的项目中应用XLua,Lua文件个别是每个Lua文件都打一个AssetBundle包,还是所有Lua都打一个AssetBundle包里?直观感觉上每个Lua都独自打一个AssetBundle包比拟不便热更新。</strong></p><blockquote><p>A:如果Lua文件数量很多(十万级别),倡议不要打Bundle,因为Bundle Header中要存储、反序列化的Asset信息过多,内存会吃不消,所以最好换成自定义的文件形式。参考星铁的Lua。</p><p></p><p>如果数量少,间接一个Bundle就足够。这样加载也不便:从一个固定的Bundle里Load Asset。</p></blockquote><p><strong>感激James@UWA问答社区提供了答复</strong></p><p>封面图来源于网络</p><hr/><p>明天的分享就到这里。生有涯而知无涯,在漫漫的开发周期中,咱们遇到的问题只是冰山一角,UWA社区愿伴你同行,一起摸索分享。欢送更多的开发者退出UWA社区。</p><p>UWA官网:www.uwa4d.com<br/>UWA社区:community.uwa4d.com<br/>UWA学堂:edu.uwa4d.com<br/>官网技术QQ群:465082844</p></article> ...

February 27, 2024 · 1 min · jiezi

关于ui:UWA学堂汇总看这一篇就够啦

学堂自成立以来,曾经上线了近300门课程,涵盖了游戏开发中的多方面内容。随着课程数量的减少,如何能力更快地找到咱们须要的课程呢? UWA学堂:edu.uwa4d.com一、搜寻最简略间接的就是依据 关键词/作者 去搜寻对应的课程内容。 二、依据分类检索点击下拉箭头,可开展分类: 本期咱们依据分类,别离举荐了局部课程,更多内容欢送登录UWA学堂:edu.uwa4d.com 进行查看。心愿大家都能学有所得,学有所成! 渲染《屏幕空间反射与PBR的渲染联合》 该课程不仅介绍了屏幕空间反射的算法,为了让渲染成果更实在,并且能够模仿更多的材质外表反射,我将在根底的SSR技术之上,思考减少PBR(基于物理外表的渲染)其中的一些影响因素: 粗糙度菲涅尔反射环境光漫反射 + 环境光镜面反射渲染方程中的cos项蒙特卡洛积分后果退出以上影响因素后,不仅能够渲染润滑的外表(玻璃,水面,镜面),也能够模拟出毛糙外表反射(高空,桌面,磨砂玻璃等等),能够满足更多我的项目需要。 《基于Unity Compute Shader实现Ray Tracing》 本文次要介绍了如何利用Unity的Compute Shader实现照片级别的渲染器,并给出了渲染器的GitHub我的项目地址供大家学习。不少对Ray Tracing渲染感兴趣的敌人用了CUDA等工具开发,工作量相当大。作者间接应用大家较为相熟的Unity,升高了工程上的开发成本,着重于Ray Tracing自身的算法和性能实现。本文将介绍Ray Tracing算法的必备知识点,以原理作为前提,再介绍如何一步一步实现渲染器的各个性能,包含场景构建,BVH,光源采样,物理材质,图像输入等,并介绍了往年风行的Wavefront内核的实现。 案例展现: 《Unreal挪动渲染管线解析》 近两年随同Unreal移动游戏在市场的不俗体现,越来越多的研发团队开始关注Unreal的挪动端开发。本次报告将聚焦Unreal挪动端的渲染管线,联合源码剖析渲染相干的模块框架以及与渲染线程的交互,并心愿帮忙研发团队理解如何扩大渲染性能,以及在优化时疾速定位CPU端的渲染瓶颈。 UI《疾速入门FairyGUI》 课程次要解说了一个残缺UI界面的开发流程。从UI编辑器方面动手学习,通过拼装一个残缺的UI逐渐相熟FairyGUI编辑器的应用;再进入Unity引擎学习UI事件的根本要点,并针对UI事件的制作进行解说;最初学习UI DrawCall相干的常识,以及制作一些有特色的UI成果。 本课程属于根底课程,适宜有肯定游戏引擎应用根底的读者。 《UIToolkit下一代UI零碎》 UIToolkit是Unity打造的全新UI零碎,它同时反对运行时与编辑时的UI界面开发,新版本引擎自带布局将全面应用UIToolkit替换,官网曾经明确示意后续禁止再提交IMGUI的代码,运行时界面将取代原有的UGUI零碎。自Unity 2020.2起UIToolkit已内置在Unity引擎中并非package包的模式,位置未然超过UGUI。 UGUI曾经好几年没有更新了,性能问题始终饱受开发者诟病,还有图文混排、循环滑动列表一类的根底组件缺失,导致每个我的项目都要独自实现一遍。在性能上UIToolkit更是超过UGUI,它能够1次DrawCall实现所有界面的绘制,UIBuilder编辑器对界面布局上也对策动美术更加敌对。 《详解UGUI DrawCall计算和Rebuild操作优化》 本文次要分为以下内容,帮忙大家深度理解UGUI的重要原理和优化办法。 解说UGUI DrawCall的工作原理,并联合大量实例解说Unity引擎如何计算DrawCall以及如何通过合批来升高DrawCall。 形容用于ScrollView的Mask和RectMask2D的具体原理以及它们是如何对DrawCall产生影响。 重点讲述咱们在优化UGUI时常常被疏忽的问题:Canvas Rebuild和动静拆散、粒子系统与UI界面联合、UI零碎的Raycast Target以及界面OverDraw的相干优化点。 资源管理《空幻引擎资产治理之道》 空幻引擎对于游戏资产治理的工作流是较为简单而繁琐的。本次报告将介绍如何扩大空幻引擎的资产管理工作流的教训,使其同时满足国内和出海发行的需要,成为一套高可靠性、可增量的疾速资产工作流;并使得安装包和更新补丁尺寸最小化至极致,同时进步运行时资产加载效率的办法。 《UWA本地资源检测应用办法与规定原理》 UWA本地资源检测是对游戏、VR等我的项目工程的资源、代码和设置等进行自动检测的插件与服务产品,是我的项目研发继续集成、继续交付流程中的重要工具,旨在为游戏研发制订资源与代码标准,帮忙研发团队疾速发现和解决我的项目中的性能问题与可能呈现的异样、谬误。研发团队可通过日常的主动查看,标准程序、美术成员的开发,从源头上对我的项目进行优化,躲避危险,节约老本。 本系列课程旨在帮忙大家更好更快地将本地资源检测使用到我的项目当中,内容次要包含:根本介绍、用法演示和规定解说等,以连载的形式更新,同时会随着产品的迭代以及大家的需要减少新的内容。 《基于Addressable资源管理零碎的大地形加载办法探索》 Addressable资源管理零碎是Unity引擎为开发者最新打造的资源管理解决方案。在本次报告中,咱们将介绍该资源管理零碎的基本功能,并重点解说如何通过它来打造一个大地形的加载和卸载治理计划,并以此来总结其与传统罕用资源管理办法的区别与劣势。通过本次报告,研发团队能够对该零碎有一个全面、清晰地理解和把握,在研发工作中能够更好地对其进行应用。 逻辑代码《【ET 8.0版本】ET框架 - C#全栈式网络游戏开发框架》 (课程仍在连载更新中,未完结)因为ET框架8.0版本进行了一系列重大的降级,架构失去了全新的迭代。大多数初学者与客户端开发人员会在首次接触ET框架时,会因为不足网络编程通识和其本身固有的面向对象开发习惯,从而被劝退或者编写出极其”不标准“的ET框架业务代码。并且因为ET框架8.0版本引入的多线程纤程的机制,进一步加深了ET框架的了解和应用的门槛。 所以本门课程,会重点围绕ET框架8.0的全新个性,对其原理与设计思路开展解说,并通过小案例的模式,帮忙读者梳理ET框架的设计思路与开发思维,从而进一步学会和把握ET框架的根本应用,并率领读者把握和相熟应用ET框架8.0版本进行游戏前后端全栈式开发的根本流程。 《DOTS-ECS系列课程》 课程共分为四个章节,笔者心愿能以一种欢快的形式带着大家去学习DOTS,领有更好地应用DOTS-ECS的能力,理解DOTS中的各种“坑”以及各种解决问题的技巧。 第一章根底内容篇,以深浅适度的独到办法使大家疾速学会学习DOTS所必须的基础知识。 第二章零碎设计篇,以一个我的项目中最必须的要害零碎(战斗环境重置零碎)为实战用例,率领大家初步尝试DOTS的应用形式。 第三章资源零碎篇,再次以一个我的项目中必须筹备的大型计划(GameObject与ECS混用计划)为实战用例,手把手率领大家从会用DOTS到把握DOTS。 第四章是不定期更新的反对章节,次要会更新一些难以归类,我的项目中又必须波及的琐碎常识。目前更新了BlobAsset相干内容(提供ECS端应用配置的办法)。 《从零开始的Unity魔法学堂》 此系列课程从最根本的概念介绍开始,涵盖C#、Unity的核心内容,从零开始带你逐渐上手Unity,助力你制作出本人的游戏,上道根本的Unity开发。 课程并不像传统教程那样应用操作录屏+PPT展现的模式讲述,而是通过精心编写的文案、粗劣的演示动画和示例、乏味的魔法比喻来将知识点压缩在短短的几分钟内,让你疾速高效又不打折扣地获取常识,快来开启属于你的魔法之旅吧。 课程体系:1、基本概念:包含软件装置、根本界面、外围流程等2、C#:包含根本语法、面向对象等Unity开发所需的语言外围3、Unity:蕴含根本架构、外围零碎、数据处理等Unity罕用内容 ...

February 22, 2024 · 1 min · jiezi

关于ui:四大技巧教你如何加速手动UI测试

用户界面测试(UI测试)是软件开发过程中的一个重要组成部分。单元测试和集成测试能够确保代码失常运行,UI测试则帮忙您确保用户在应用过程中获得最佳体验。这些测试有一个独特的挑战——创立和保护可能十分耗时,尤其是手动测试。 一起来看一下减速手动UI测试的四种办法,以及为什么您须要思考测试自动化。 办法一:放大关注范畴大多数企业的测试资源无限,手动UI测试自身曾经是一项繁琐的工作,因而,放大关注范畴并进步测试的优先级是十分重要的。 基于危险的测试是一种罕用的办法,它侧重于测试绝对危险最高的缺点。 一个高频率、高影响缺点的典型例子是明码重置流程呈现问题。当用户尝试重置明码时(这是很常见的行为),一旦他们被锁定无奈登录应用程序,影响将是灾难性的。因而,测试明码重置性能应该被优先思考,因为绝对危险较高。 要施行基于危险的测试,能够将每个测试用例绘制在一个图表上,其中X轴示意性能的影响,Y轴示意产生的频率。手动UI测试应该从呈现在右上象限的测试用例开始,并逐步向左下象限挪动。这样才阐明您是在通过测试升高危险,而不是在随便地调配它们。 办法二:始终应用清单阿波罗11号宇航员迈克尔·柯林斯(Michael Collins)将清单称为宇宙飞船的“第四名机组成员”,事实上,它是真正的指挥官。从宇航员到外科医生,清单都能加重认知负荷,确保每次都正确地执行所有,如同发条个别。 手动UI测试也不例外。 大多数UI测试用例都包含了根本的信息,比方要测试的UI事件、一些测试条件和预期的后果,然而最好的测试用例会蕴含具体的清单。 例如,您可能心愿有一个逐渐的过程查看清单,帮忙确保正确执行UI测试,以及列出用于测试特定动态数据的数据源列表。这些清单确保手动测试人员不会漏掉任何关键步骤,从而使防止测试后果的不精确。 除了这些清单,您还能够应用工作流清单,确保bug被正确地记录在bug跟踪器中,并调配给开发人员进行后续跟进。 例如,您能够在清单中增加生成屏幕截图,包含状态代码,或者提供其余有用的信息,帮忙开发人员疾速诊断问题,不用浪费时间重现问题。 办法三:应用根底脚本实现自动化比起编写脚本,许多手动UI测试人员更相熟质量保证流程。他们可能不相熟浏览器自动化工具,如Selenium或挪动测试自动化工具。这些工具可能并不适用于所有场景(例如探索性测试或UX测试),但在某些状况下,根底脚本可能是价值连城。 TestComplete使得构建自动化测试变得简略,它甚至不须要您编写任何代码。无论技能程度或编码教训如何,应用录制和回放或应用要害驱动测试,就能够疾速轻松地运行功能测试。只需录制一次测试,就能够在各种桌面、Web和挪动技术堆栈上运行这些测试,确保一切正常运行。 与许多其余反对录制和回放性能的测试自动化框架不同,通过TestComplete的要害驱动框架,非编程人员能够应用非技术关键字轻松定制录制步骤。您能够定制的局部包含拆散测试对象、操作和测试数据等,您也能够在其余测试中重复使用这些元素。 办法四:自动化功能测试UI测试自动化通常被认为是一项低廉且耗时的工作。相干人员可能不想期待手动UI测试过程,测试工程师难以保护每次设计迭代中都须要批改的软弱测试,开发人员可能会因误报而感到丧气。因而,只有执行切当,每个人都能够领会到UI测试的价值。 TestComplete以三种不同的形式解决了这些挑战,让你在麻利开发工作流中充分利用UI测试的劣势: 记录和回放性能,意味着创立UI测试所需的工夫仅为应用简单脚本创立测试的一小部分;AI驱动的对象辨认,意味着UI测试能够跟上一直倒退的用户界面,而无需破费数小时重写代码使其通过;继续集成反对,意味着UI测试能够轻松地与Jenkins或其余CI平台,以及Jira和其余报告平台集成。在探索性测试或用户体验测试等畛域,手动UI测试始终有其存在的地位。但功能性UI测试应该自动化,这将大大减少工夫和老本。这些测试遵循着更容易预测的模式,能够应用TestComplete或其余UI测试工具轻松地将其转换为自动测试。 最重要的是UI测试以老本低廉且耗时而闻名,但有几种办法能够升高这些老本并让你真正地从中获益。通过放大关注范畴、应用清单以及引入自动化,您能够利用到UI测试的劣势,而不会减慢麻利开发过程或减少老本。 TestComplete是业界首款具备混合对象和视觉辨认引擎的自动化测试工具,可应用原生BDD格调的Gherkin语法测试每个桌面、Web和挪动应用程序,脚本或无脚本可灵便抉择。您能够轻松进步UI测试的覆盖率,并确保交付高质量、通过牢靠测试的软件。 文章起源:https://smartbear.com/blog/try-these-hacks-to-speed-up-your-m...

June 19, 2023 · 1 min · jiezi

关于ui:Unity中级客户端开发工程师的进阶之路

上期UWA技能成长零碎之《Unity高级客户端开发工程师的进阶之路》失去了很多Unity开发者的必定。通过零碎的学习,能够把握游戏性能瓶颈定位的办法和常见的CPU、GPU、内存相干的性能优化办法。 UWA技能成长零碎是UWA依据学员的职业倒退指标,提供技能学习的举荐门路,再将所需学习内容按难易等多维度,设计分成多个学习阶段,能够循序渐进地进行学习。 本期咱们梳理了Unity中级客户端开发工程师的进阶之路,带大家一起理解,此指标须要把握哪些技能?学习门路按难易度进行了辨别,帮忙大家一步步进阶,晋升自我。 注:数字1~3 示意难度顺次递增 难度1:UGUI、NGUI、物理、C#编程、Lua 难度1. UGUI-根底 《UGUI开发根底与实际》 该课程通过对Unity引擎UGUI的根底介绍联合具体的实例制作,零碎、具体地解说了UGUI开发的各项实用知识点,次要包含:UI界面布局、自定义Shader成果、UI组件扩大、UI事件零碎搭建,以及罕用的性能优化办法等。 讲师会手把手率领学员把握如何搭建高效和酷炫的3D UI零碎,并在实践中了解其中波及到的基础知识、运行机制,以及进阶技巧。 《UGUI深度钻研之源码鉴赏》 从UI的代码底层讲起,通过了解底层代码的原理来认知下层的无效优化办法。这里会从源码中深入分析UI网格重建的实现原理,并实现了提取触发UI重建的UI元素的代码实现,能够更不便更精确地定位引起UI性能问题的UI元素,从而有针对性地做优化。 在剖析Image、Text、Layout等局部源码的同时,也提出应用的注意事项。使开发者在UGUI制作及性能优化方面,更好地达到“知其然,知其所以然”的成果。 “UI开销如何优化?”、“如何定位引起重建的UI元素?”、“UGUI Text如何进行优化?”、“如何正当应用Layout?”、“在设置UI点击事件时有哪些注意事项?”等等这些常见的UI开发问题,都能够在这个课程中失去解答。 不论是行业新人,还是Unity从业者,都无妨来一起领略一番UGUI源码,置信都会有所播种。 难度2. UGUI-框架 《UI框架搭建:控件定制篇》 从入行到主程再到打杂,我遇到了许多游戏开发者,他们中很多人都已经做过或正在做UI的开发工作。当问他们感觉怎么样时,广泛的反馈都是:这项工作很简略。赶快学好这根本的能力,去做其余工作。尽管我也是从这个阶段过去的,但过后如同没这么忙。这里不深刻探讨对错,毕竟我曾经是站着谈话不腰疼,但从理论的现状来看,UI这项简略的工作招人通过率很低。 来面试的少年,左MVC、右框架设计,两年工作教训在腰间,人人都会代理绑定。大略感觉这么全面的能力,没啥情理不过吧?其实坦白讲,我做面试官时不太关怀是什么,而是关怀为什么。我的项目千千万,做成什么样都不稀奇,外围是为什么要这样做?有没有更好的做法?因而,我通常会问:你感觉NGUI/UGUI的毛病是什么?个别也会顺便问一下:看没看过UI框架源码?如果每天都用的框架底层也没钻研透,我感觉这框架设计的功力不听也罢。 扯远了,说回这个课程。咱们的宗旨是聊聊如何正当地思考问题。不过尬聊切实不合乎时代的快节奏,就拿UGUI开刀,讲讲我认为框架搭建的正确姿态。这门课程算是进阶课程,不会提根本的UI常识,因而倡议有肯定根底的同学购买。 《UI框架搭建:音讯重构篇》 在理解了消息传递原理和UGUI根底框架的扩大办法后,这节课我和大家一起搭建个实用的UI音讯框架。UI框架其实涵盖了很多内容,咱们这里只探讨音讯框架。很多读者必定会说,绑定音讯不就是AddListener嘛。说的对,但我这里说的不只是这点。实质上说,AddListener是更底层和原始的音讯应用形式。毕竟大家都是在做商业我的项目,打仗就要业余,不然会出事的。 和《UI框架搭建:控件定制篇》雷同,除了搭建可能独立运行的Demo,在课程中也谈判及面对问题时以何种角度思考。本次内容是在《UI框架搭建:控件定制篇》的根底上深刻开发,尽管没有铺垫也能够看懂,但还是倡议先看控件定制篇。这门课仍然是进阶课程,不会提及根本的UI常识,倡议有根底的同学购买。 《UI框架搭建:利用技巧篇》 在理论应用中,咱们不可避免地会扩大现有的框架,并增加一些与业务耦合性较强的脚本,这种状况下如何保障涣散的耦合关系呢?在应用框架的过程中,咱们还有哪些须要留神的中央呢?这节课咱们就探讨一下这些问题。 和后面的课程雷同,本节课会介绍编码技巧以及计划取舍的逻辑。本次内容是在后面的根底上深刻开发,尽管没有铺垫也能够看懂,但还是倡议先看《控件定制篇》和《音讯重构篇》。这门课程仍然是进阶课程,不会提及根本的UI常识,倡议有根底的同学购买。 难度2. UGUI-UIToolkit 《UIToolkit下一代UI零碎》 UIToolkit是Unity打造的全新UI零碎,它同时反对运行时与编辑时的UI界面开发,新版本引擎自带布局将全面应用UIToolkit替换,官网曾经明确示意后续禁止在提交IMGUI的代码,运行时界面将取代原有的UGUI零碎。自Unity 2020.2起UIToolkit已内置在Unity引擎中并非package包的模式,位置未然超过UGUI。 UGUI曾经好几年没有在更新了,性能问题始终饱受开发者诟病,还有图文混排、循环滑动列表一类的根底组件缺失,导致每个我的项目都要独自实现一遍。在性能上UIToolkit更是超过UGUI,它能够1次DrawCall实现所有界面的绘制,UIBuilder编辑器对界面布局上也对策动美术更加敌对。 难度2. 物理-模仿 《基于网格的GPU流体模仿》 文章次要介绍了基于网格的流体模仿的基本原理及其实现形式,分为4个局部。首先,简略介绍了流体模仿的概念,再是介绍相干的数学根底,而后在实践上从流体方程Navier Stokes动手,对其合成解析,逐渐剖析公式的每一部分的数学概念和解算过程,最初在Unity上通过Demo实现流体模仿的每一部分的计算并进行解说。 本课程属于进阶课程,解算过程绝对艰涩,适宜有肯定的图形学根底、数学根底或者Shader编程教训的读者。 难度1. C#编程-根底 《C#常识体系构建》 市面上有很多书籍和专栏是介绍 C# 根底或者进阶个性的,无一例外,都是从头到尾列举一遍 C# 个性,而后针对一个个个性去进行解说和介绍,这种浏览体验绝对会比拟干燥。浏览一些大部头的书籍又要占去了大量的业余时间,而有些读者仅仅是想晓得其中的某一个语法个性或者仅仅是想达到一个可能开发我的项目的水平,在读者过后所处的阶段兴许并不需要齐全地理解 C# 的每一个语法细节和原理。基于以上的种种原因,本课程诞生了。 通读本课程,读者不仅可能过一遍罕用的 C# 语法个性,还能够目击一个常识体系构建的过程,最终还能够失去一个根本的 C# 常识体系。有了一个根本的常识体系之后,将来 C# 公布新的个性时,咱们能缩小一些恐慌的情绪,晓得这个新的个性兴许只是一个小的改良,兴许是一个变革的个性,能够非常容易地把这个个性归纳到咱们现有常识体系中,这就是常识体系的一个小作用。在笔者介绍 C# 的语法个性的过程中,笔者会破费更多的篇幅去介绍不怎么罕用然而很重要的 C# 个性,一些大家比拟理解且罕用的,尤其是在网上轻易一搜就能找到的语法个性笔者则会简略带过。 如果想要有一个根底的 C# 常识体系或者好奇一个常识体系是怎么产生的,又不想破费太多工夫去看大部头的同学,十分举荐购买本课程。然而并不是说,学习完本课程后就能够不必看大部头了,大部头还是要看的,不过能够留到工夫十分富余的时候再去看,而且学习完本专栏之后再去看大部头会节俭更多的工夫。 难度2. C#编程-框架 《ET框架 - C#全栈式网络游戏开发框架》 ...

May 18, 2023 · 1 min · jiezi

关于ui:快速入门FairyGUI课程介绍

本期举荐课程次要解说了一个残缺UI界面的开发流程。从UI编辑器方面动手学习,通过拼装一个残缺的UI逐渐相熟FairyGUI编辑器的应用;再进入Unity引擎学习UI事件的根本要点,并针对UI事件的制作进行解说;最初学习UI DrawCall相干的常识,以及制作一些有特色的UI成果。 本课程属于根底课程,适宜有肯定游戏引擎应用根底的读者。小伙伴们能够到UWA学堂查看全文! https://www.youku.com/video/X...

February 27, 2023 · 1 min · jiezi

关于ui:使用SelectorUI搭建SelectorUIAPP

筹备工作装置 Selector-UI在 Ponmenty OS 中,咱们通过 nty install selector-ui-3 指令装置Selector-UI。在装置过程中,可能会提醒 [nty]PonmentyOS所依赖的装置器证书未失常装置。installer.okkept.app.app证书的授予人未授予本计算机的相应证书。以上谬误的产生起因是Selector-UI的证书反对的最新版本是PonmentyOS Client 2020(性能差),而目前咱们通常应用Server 2021以上。咱们须要批改这个证书以装置。 批改证书第一步:取得本地相应证书 nty KeyManager -where installer.okkept.app.app -l[nty]该证书存储于/nty/key/3788.5321.23.1/中nty dir /nty/key/3788.5321.23.1/[nty] 0/0 .keynty editor -get /nty/key/3788.5321.23.1/.key[nty]该文件被长期保留在/nty/editor/files/S9oV1.key中第二步:批改证书 nty KeyManager -edit -version[nty]KeyManager KeyEditor .23/*注:如果小于19版本则须要应用nty KeyManager -updata*/nty KeyManager -edit file:/nty/key/3788.5321.23.1/.key -code[nty]文件内容如下:ipConfigBase64:....keyName:3788.5321.23.1uto:3788.5321.23.1:10codeMd5Base64:....第三步:批改装置器 nty KeyManager -edit remote installer.okkept.app.app code:下面的codeMd5Base64装置Selector-UI-APP在手机端利用的开发中,咱们通常应用Sun Der或者webcat开发手机端响应的selector-ui。Sun Der的长处是:前后端交互数据安全,开发速度快,领有网页沙盒(象征也能够用ios中)webcat的长处是:启动速度快,编辑不便 本文抉择Sun Der搭建APP。 nty okkept -install sun-der-selector-ui-appnty okkept -install sun-der-backend-manager装置网页沙盒 nty sunder -install webpage-iobox编辑APP创立我的项目在终端中为Selector-UI创立web服务。 nty server -http://*:8080 /nty/dev/okkept/selector-ui/web/app.webconfig而后咱们能够通过拜访http://服务器ip:8080/,而后会呈现 Welcome to Selector-UI欢送来到 Selector-UIsun-der-selector-ui-appsun-der-backend-manager-APP -S -sunder -sunder.app.app -webpage-iobox拜访 http://服务器ip:8080/APP/S/sunder/sunder-app-app/webpage-iobox ,就能够批改iobox的信息了。具体各项解释请百度安卓APP构造。 ...

February 11, 2023 · 1 min · jiezi

关于ui:深入UGUI-Mask组件原理和性能深度优化

对于一款游戏UI零碎必不可少,UGUI是当初项目组中实现UI零碎的大多数解决方案,应用宽泛。UGUI提供的组件很多,内部很多厂商也提供了很多UGUI的第三方插件,而客户端开发人员往往适度关注我的项目进度实现性能,对各个组件仅仅停留在“会应用”这个根本层面上,并没有对组件自身的实现原理、优缺点以及性能方面做深刻理解。  该课程以性能备受争执的Mask组件为例,从Mask组件的实现原理动手,深刻到CPU、GPU和GC上Mask产生的影响,而后给出解决方案,最初给出在UI上应用3D模型,如何应用Mask原理对其进行遮罩解决。  Mask组件实现原理以及存在的问题Mask组件的性能以及优化(CPU、GPU、GC)如何用Mask组件来给3D Object做遮罩 作者于洋,Unity技术专家、引擎组组长。曾就任于人人网、Kabam、竞技世界。从事游戏开发十余年,经验了从Flash到Unity的游戏开发过程,长期从事游戏渲染和性能优化相干工作,对PBR、云、雾、地形、URP管线等有深入研究,曾参加过《Legacy of Zeus》、《荒岛求生》、《mythwar puzzle》等游戏的渲染和性能优化工作,乐于分享渲染和优化的相干技术。 目录 1|概述2|Mask组件实现原理以及存在的问题3|Mask组件的性能以及优化4|如何用Mask组件来给3D Object做遮罩5|论断 本篇转载自《深刻UGUI Mask组件原理和性能深度优化》的第1节。  1|概述 遮罩不是一种可见的UI控件,而是一种批改控件子元素外观的办法。遮罩将子元素限度(即“覆盖”)为父元素的形态。因而,如果子项比父项大,则子项仅蕴含在父项以内的局部才可见。  领有Mask组件的UI控件,能够限度其子对象的显示范畴,即当子对象的显示范畴显著大于父对象的显示范畴时,游戏视图就只显示父对象范畴内的子对象,其余局部自动隐藏。  咱们在Canvas上面新建一个2D UI Image,抉择一个带有形态图案的Sprite,在该物体上Add Mask Component,这样,它就变成了一个遮罩物体。以该物体作为父对象,在其上面新建一个2D UI Image作为子物体,抉择一个Sprite,这样就实现了一个简略的Mask组件的应用,后果如下图: 留神到Mask组件里有个选项“Show Mask Graphic”,官网的形容“是否应在子对象上应用Alpha绘制遮罩(父)对象的图形?” 意思就是是否绘制出Mask组件上的Image图形,如下图: 勾掉后,就不会再绘制出该Image,具体无关的性能问题,在后续会具体探讨。  以上根本介绍了一下UGUI Mask组件以及最根底的应用办法,在应用办法上,大家作为Unity的开发人员必定是特地相熟。  然而在理论游戏开发中,咱们不仅仅要关注于我的项目的性能开发进度,如果游戏的性能不能满足要求,经常出现卡顿、闪退等问题,用户体验重大降落,就算开发出再好的游戏玩法也没方法满足玩家的体验需要。  性能优化是游戏我的项目开发中一个重要且必须的元素。而即使在硬件设施高速倒退的明天,随着用户和我的项目的需要的持续增长,对游戏特效、画质、高度简单且实在的场景的需要也在逐步榨干硬件性能,无论大公司还是小公司,无论研发团队有如许丰盛的开发教训,性能优化这件事永远是一个十分辣手而又无奈绕开的问题。  那么咱们在进行游戏开发的时候,对我的项目中所应用到的组件、库、插件、Unity built-in的性能的过程当中,要时刻警觉其性能问题:有没有给我的项目带来重大的性能降落?如果呈现了性能降落了,是因为应用了哪个性能导致的?有没有其余的计划能够作为代替?  Mask组件就是其中之一,咱们在应用的时候要一直地进行性能评估。 你是否在理论开发中有过上面几个疑难: 在应用了Mask组件之后,DrawCall为何减少特地重大?这些DrawCall的起源是哪里?应用Mask组件做遮罩的视觉效果其实并不现实,有锯齿?网上说的应用Mask组件的子对象没方法和外界做合批处理,低层的起因是为什么?如何对Mask组件进行优化以满足本人我的项目的性能要求? 接下来的章节会一一具体解答这些问题。 以上就是《深刻UGUI Mask组件原理和性能深度优化》的第1节,此篇文章比拟适宜从事游戏开发的Unity客户端开发人员、心愿晋升渲染和性能优化能力的人以及对性能优化感兴趣的同学。  读完全篇后你会深刻理解UGUI源代码和实现逻辑,并把握Mask组件对模板缓冲区的应用。  作者的另一篇文章《挪动端GPU性能深度优化剖析》也同时上线,组合购买仅需19.9元。

January 16, 2023 · 1 min · jiezi

关于ui:2022-倒带-NutUI

京东批发 于明明前言时光飞逝,流年似水,让咱们倒带 2022,回首这跌宕起伏一年走过的 “降级之路”。 NutUI 体现如何? 成绩单等着您打分! 2022 是 NutUI 在技术长廊中摸索和成长的第四个年头,悄悄度过了本人的“孩提“时光。NutUI 也从繁多 Vue 技术栈的组件库逐步成长为适配多端的多技术栈的组件库,降级设计规范「京东APP视觉」「京东科技视觉」,适配 Taro 框架反对小程序开发,拓展 React 技术栈,打造 NutUI 业务组件生态等。 2022 是不平庸的一年,新冠末年前端技术也急剧改革,低代码的风起(Retool),D2C 和 AI(codefun & ChatGPT) 的涌动,对各行各业的开发者带来了很大的冲击。然而 NutUI 初心未变,做好 UI 组件,服务于原始的代码开发者,同时亦为低代码平台提供撑持,就在这一亩三分地里精耕细作。 2022,咱们相继实现了 React 技术栈的公布、多端小程序的适配、对组件性能进行比拟并补齐、减少单测、丰盛主题定制、齐备国际化性能、推出 IDE 插件,在线代码调试等性能。目标只有一个:「打造一款好用的挪动端组件库,为开发提效,为业务赋能,为开源奉献一份力量」 React 技术栈裁减这一年,咱们实现了 React 技术栈的裁减。 NutUI 自公布以来都是以 Vue 技术栈为底座进行迭代和降级,随着团体外部及社区对 React 版本的呼声越来越高。2021 年 6 月咱们启动了 NutUI-React 布局并随即进入开发。 2022 年 1 月,公布 NutUI-React 1.0版本。基于 React 17 和 更快的构建工具 Vite,全面应用 TypeScript, 组件规模 60+。下半年咱们继续加码,修复问题 160+,降级到 React18,组件规模最终达到 70+,对齐了 Vue。NutUI-React 的设计与 Vue 版保持一致,诸如我的项目架构,小到组件的 props、events 都放弃产品的统一性。 ...

January 3, 2023 · 2 min · jiezi

关于ui:如何成为优秀的UI设计师

如何成为优良的UI设计师如何成为ui设计师,如何成为一名优良的ui设计师? 随着互联网浪潮的一直倒退,ui设计师这个职业也被越来越多的人所器重,成为了煊赫一时的职业。 在批发,交通,金融等行业都能够见到ui设计师的身影。那么,如何成为ui设计师?明天咱们就来讲讲这个问题。 要想成为一名UI设计师,这无疑是一个漫长而又辛苦的过程,UI设计师须要十分多的创意,而创意源于灵感和悟性,这些都来自平时的积攒。 刚入行的UI设计师们须要一直地磨难本人,被动造就本人寻找灵感、积攒灵感,有时候的一个灵光一现,或者就会成为大家拍案叫绝的作品。 UI设计师只有本人对本人严格要求,能力更好的进步本人;并且长于察看,长于发现,让你的大脑始终处于沉闷状态,这样可能更容易触发灵感。 成为UI设计师所要具备的根底条件1、视觉设计根底首先要造就肯定的美术功底,绘画,构图,色调等基本知识,是一个UI设计师的根底。 2、WEB界面端的设计,PS网页设计根底,网站页面设计根底,网站banner设计,网站产品图的前期解决,还有最热门的淘宝主页,店面的设计,以及淘宝SEO的理解,商务网站的设计等等。这里也是设计师最出彩的中央。 3、挪动端H5的动画设计。4、挪动端的界面设计,Adobe Illustrator CC2015的学习,ISO图标设计,掠影其中图标,扁平化图标的设计,安卓零碎UI设计指南,安卓图标的设计等。 5、交互设计,用户体验感的感知,为更好的设计出脍炙人口的产品做出致力。 6、动效设计。通过AE等软件的学习,制作出动静的设计方案,让客户更好的体验设计理念,也更好的宣传设计。 如何成为优良的UI设计师1、对本人有明确的定位和指标 理解本人善于的畛域,并正当使用本人的短处,这会给你的工作带来事倍功半的成果。有本人的指标,就有奋斗的信心,用心实现好本人的每一个作品,为本人累积教训财产。 2、基本功不能失落 UI设计师的基本功是什么?是手绘,当初各大软件、APP层出不穷,能够很好的辅助UI设计师的工作,节俭工作工夫。但,设计师们也不能因而旷废了本人的手绘能力。有时候一张纸、一支笔可能比智能化的工具来得无效。举个例子:一位UI设计师在和用户沟通需要的时候,用户想要疾速的看到大体的成果,这时候你齐全能够用笔在纸上画出框架图,依据用户的需要做更改,明确理解之后,在用电脑软件制作进去。 3、收集素材的能力 首先要学会从各大设计网站去寻找素材,学会关键词搜寻。而后要建设本人的素材库,倡议各位UI设计师们把收集到的素材整顿到一个文件夹里,并且做好分类,以便于查找调动。 4、从伪原创开始 在你没有任何思路的状况下,无妨看看大师级别的作品,去剖析他们的作品的优良之处,而后再学以致用,去模拟他们的作品,使本人疾速提高。 5、敢于尝试新的货色 设计巨匠靳埭强说:设计源于生存,创意滋润常在你我身边。很多设计师都晓得,创意最大的起源是“体验”。生存中的“体验”将帮忙你最大化本人的思维。 谈谈我的认识打造一个产品,UI、开发、产品、测试等等,都是很重要的,那咱们这里就单纯说说UI吧。 UI是一个很重要的位置,当然,一个研发团队,每个人都很重要,作为一枚优良的UI设计师,首先翻新是必要的,不要胆小,突破规定,有时候会发现更好的设计。 其次,要符合实际,有些货色,你设计是十分十分好的,用户也称心,然而前端开发工程师并不是肯定能够实现或者实现出完满的成果,那这样的设计势必意义不大。 所以,多和团队交换,才能够打造最好的设计。 另外,你要去理解用户,去关注很多人,他的偏好,再来依据偏好设计产品,切忌独断独行。 尾述"不积跬步无以至千里,不积小流无以至江海”。如何成为UI设计师,如何成为一名优良的UI设计师?这当然离不开日常生活中的积攒,灵感来源于生存,任何一件小事都有可能激发出创作的灵感。除了日常的积攒之外,也要有扎实的基本功,正所谓宝剑锋从磨砺出,梅花香自苦寒来。

October 19, 2022 · 1 min · jiezi

关于ui:达内UI全链路设计2022年最新完结无秘

download:达内-UI全链路设计-2022年最新完结无秘nt Design Vue 是一个十分成熟的框架,应用 Ant Design Vue 创立用户界面非常简单,这些组件能够适应各种图标款式、字体和彩色主题。Ant Design Vue 不断改进其60多个组件,根本笼罩我的项目大部份需要,而且使它们变得更好,更易于拜访。 Vue3 上的 Ant Design 包更小,感觉更轻,并且反对 SSR(还包含组合API),Ant Design 领有成熟的简单组件,如数据表、统计框、pop确认、模态和弹出窗口。Ant Design Vue 在 GitHub 上领有 15k+ 颗星,每周下载量为 49k,数据曾经阐明了它的受欢迎水平。BalmUI官方网站:next-material.balmjs.com/#/BalmUI 曾经公布了他们的 9.0 版本了,该版本反对Vue3。Balm 基于谷歌的 Material Design,这就是为什么它看起来很相熟。Balm 附带 Vue 插件和指令,以及从简略到简单的高度可定制的组件。 BalmUI 倒退十分迅速,如果想应用 Material Design 格调,应用简单的内置指令(如 debouncing 和 UI 波纹)在创立自定义组件时能派上用场,那么它非常适合 Vue3 我的项目。Wave UI官方网站:antoniandre.github.io/wave-ui/WaveUI 在Vue3 公布后进行了良好的定位,WaveUI 的开发是在 Vue3 仍处于alpha阶段时就开始了,其指标是在其API稳固后立刻反对它,使其成为首批 Vue3 UI框架之一。 WaveUI 领有40多个丑陋且响应迅速的组件,它们的范畴从旋转器到日历,以及介于两者之间的任何货色。WaveUI 还提供实用程序、可定制性和成熟的集成表单验证性能。WaveUI 提供的组件十分丑陋,动画成果也十分好,它的格调在整个框架中是统一的。企业级响应式 Vue3 应用程序不错的抉择。Vuestic官方网站:vuestic.dev/Vuestic 是 Vue 最丑陋的开源治理面板之一,善于编写可保护的 Vue 代码,制作灵便的组件和接口。 ...

September 2, 2022 · 1 min · jiezi

关于ui:达内UI全链路设计2022年最新完结无密

download:达内-UI全链路设计2022年最新完结无密JavaFx Tooltip悬浮提醒使用及自定义(Kotlin)使用Tooltip一般是和某个节点控件绑定使用,这里的节点控件只能是control中的包javafx.scene.control,粗疏可能点击地址跳转查看除此之外,还提供了另外一个方法,可能让tooltip在以后窗口的指定地位浮现,比较省事的就是需要自己去计算偏移量 Tooltip.install(control,tooltip) 绑定使用tooltip.show(window,x,y) 指定窗口和偏移量浮现 PS: tooltip指的是Tooltip的对象,可能间接新建 Java中使用简略地提下Java中的使用//control是某个控件Label label = new Lable("hello")Tooltip.install(label, new Tooltip("鼠标悬浮浮现的文字"));复制代码TornadoFx中使用class TestView : View("My View") { override val root = vbox { label("hello") { tooltip = tooltip("这是一段解释说明") }}}复制代码自定义本章节包含对tooltip的样式定制化以及指定窗口浮现样式自定义默认的黑底白字有些丑,有时候感觉看到不太明显,咱们可能对其样式停止调整,调整为白底黑字,代码如下:label("hello") { tooltip = tooltip("这是一段解释说明"){ style { backgroundColor += c("white") textFill = c("black") }}}复制代码成果如下图所示: 从下面的代码其实就是修改了tooltip外部的样式就可能了,举一反三,相干属性或样式修改即可对tooltip的样式停止调整label("hello") { tooltip = tooltip { prefWidth = 200.0 textAlignment = TextAlignment.LEFT isWrapText = true style { backgroundColor += c("white") textFill = c("black") } isAutoHide = false text = "这是一段长文本说明长文本说明这是一段长文本说明长文本说明"}} ...

August 23, 2022 · 1 min · jiezi

关于ui:vivo官网APP全机型UI适配方案

vivo 互联网客户端团队- Xu Jie日益新增的机型,给开发人员带来了很多的适配工作。代码能不能对立、apk能不能对立、物料如何选取、款式怎么展现等等都是困扰开发人员的问题,本计划就是介绍不同机型的共线计划,打消开发人员的疑虑。 一、日益纷纷的机型带来的挑战1.1 背景科技是提高的,人们对美的要求也是逐步晋升的,所以才有了当初市面上不拘一格的机型 (1)比方vivo X60手机采纳纤薄曲面屏设计,属于直板机型。 (2)比方vivo 折叠屏高端手机,提供更优质的视觉体验,属于折叠屏机型。 (3)比方vivo pad,领有优良的操作手感和高级的质感,属于平板机型。 1.2 咱们的挑战在此之前,咱们次要是为直板手机去服务,咱们的开发只有适配这种支流的直板机器,咱们的UI次要去设计这种直板手机的效果图,咱们的产品和经营次要为这种直板机型去抉择物料。 可是随着这种不拘一格机型的呈现,那么问题就来了: (1)开发人员的适配老本高了,是不是针对每一种机型,都要做个独自的利用进行适配呢? (2)UI设计师要做的效果图要多了,是不是要针对每种机型都要设计一套效果图呢? (3)产品和经营须要抉择的物料更受限制了,会不会这个物料在一个机器上失常。在其余机器上就不失常了呢? 为什么这么说,上面以开发者的角度来做介绍,把咱们面临的问题,做阐明。 二、 开发者的困境2.1 全机型适配老本太高日渐丰盛的机型适配让咱们这些android开发人员疲于奔命,尽管能够依照要求进行适配,然而大屏幕的机型适配老本仍然比拟高,因为这些机型不同于传统的直板手机的宽高比例(9:16)。所以有的利用罗唆就间接两边留白,内容区域展现在屏幕正地方,这种成果,当然很差。 案例1:某个视频APP页面,未做pad上的适配,关上之后的成果如下,两边大量留白,是不可操作的区域。 案例2:某新闻资讯类APP,在pad上的适配成果如下,可见的范畴内,信息流展现内容较少,图片有拉伸、含糊的问题。 2.2 全机型适配老本高在哪下面的案例其实只是外表的问题之一,作为开发人员,须要思考的因素有很多,首先要想到这些机型有什么特点: 而后才是须要解决的问题: 三、寻找全机型适配计划之旅3.1 计划探讨与确定页面拉伸、左右留白是景象,这也是用户的间接体验。那么这就是咱们要改善的中央,所以当初就有方向了,围绕着 “如何在可见区域内,展现更多的信息” 。这不是布局的简略从新排列组合,因为 计划相对不是只有开发决定如何实现就能够怎么实现的,一个apk承载着性能到用户手里波及了多方角色的染指。产品经理须要整顿需要、经营人员须要配置物料、公布apk,测试须要测试等等,所以最终的计划不是一方定下来的,而是一个协调对立后的后果。 既然要去探讨计划,那么就要有根据,在此省略探讨、评审、定稿的过程。 先来看看直板、折叠屏、pad的内部轮廓图,晓得页面状态如何。 3.2 计划落地示意图每个利用要展现的内容不统一,然而原理统一,此处就以上面几个款式为根底介绍原理。准则也比较简单,尽可能展现更多内容,不要呈现大面积的空白区域。 上面没有介绍分栏模式的适配,因为分栏的模式也可能被用户敞开,最终成为全屏模式,所以说,能够抉择只适配全屏模式,这样的适配老本较低。当然,这个也要依据本人模块的状况来确定,比方微信,更适宜左右屏的分栏模式。 3.2.1 直板机型适配计划骨骼图直板机型,目前支流的机型,宽高比根本是9:16,能够最大限度地展现比拟多的内容,比方下图中的模块1、模块2、 模块3的图片。 3.2.2 折叠屏机型适配计划骨骼图折叠屏机型,屏幕可旋转,然而宽高比根本是1:1,高度和直板机器根本差不多,能够达到2000px的像素,所以在纵向上,也能够最大限度地展现比拟多的内容,比方下图中的模块1、模块2、 模块3的图片。 3.2.3 PAD机型适配计划骨骼图pad平板,屏幕可旋转,并且旋转后的宽高比差别较大,纵向时,宽高比是5 : 8,横向时,宽高比是8 : 5。 在pad纵向时,其实高度像素是足够展现很多内容的,比方下图中的模块1、模块2、 模块3的图片; 然而在pad横向时,没方法展现更多的内容(倒是有个计划,最初再说),只能下图中的模块1、模块2的图片。 3.3 计划落地标准3.3.1 一套代码适配所有机型确定一个apk能不能适配所有机型,首先要解决的是要合乎不同机型的个性,比方直板手机只能纵向显示,折叠屏和pad反对横竖屏旋转。 形容如下: (1)需要 直板屏:强制固定竖屏;折叠屏:外屏固定竖屏、内屏(大屏)反对横竖屏切换;PAD端:反对横竖屏切换;咱们须要在以上三端通过一套代码实现下面的需要。 (2)横竖屏切换 ...

July 18, 2022 · 3 min · jiezi

关于ui:如何降低无效的物理开销

1)如何升高有效的物理开销2)EventSystem.Update如何优化比拟适合3)如何定位UWA报告中检测到的Standard Shader问题4)如何定位在UWA AssetBundle检测中看到n/a的网格问题 这是第298篇UWA技术常识分享的推送。明天咱们持续为大家精选了若干和开发、优化相干的问题,倡议浏览工夫10分钟,认真读完必有播种。 UWA 问答社区:answer.uwa4d.comUWA QQ群2:793972859(原群已满员) PhysicsQ:如果Unity没有用到物理模块局部,如何将这部分敞开?以及是否能够动静开关? A1:以下供参考:Physics.autoSimulation = false;Physics.autoSyncTransforms = false;//不必NGUI或者Raycast可敞开感激萧小俊@UWA问答社区提供了答复 A2:能够抉择动静敞开,也能够间接敞开Physics设置外面的Auto Simulation,如果用到射线检测或者NGUI,须要把上面的Auto Sync Transforms勾选。 感激小ben@UWA问答社区提供了答复 UGUIQ:请问EventSystem的优化怎么下手比拟适合?对EventSystem的性能优化有何倡议? A1:EventSystem的优化能够参考这篇文章:https://networm.me/2019/10/06...感激小ben@UWA问答社区提供了答复 A2:布局一下Canvas,只有须要检测才须要挂上GraphicRaycaster组件。而后EventSystem中的大部分性能可能是点击之后触发的事件造成的,并不能算EventSystem的,须要独自看一下触发的代码。感激萧小俊@UWA问答社区提供了答复 ShaderQ:请问在UWA报告中看到Standard Shader,该如何定位问题? A:Standard Shader呈现的起因,个别是导入的FBX模型中或者Unity本身生成的一些3D对象应用了自带的Default Material,从而依赖了Standard Shader。能够联合UWA在线AssetBundle检测工具排查是哪个AssetBundle包中哪些资源援用了Standard Shader,如下图: 在找到了是哪个FBX资源带有Default Material后,能够用Unity提供的AssetPostprocesser类的回调函数对其进行剔除,如下图,是一个简略的做法。 感激Faust@UWA问答社区提供了答复 AssetBundleQ:在UWA 在线AssetBundle检测后果中看到n/a的网格,请问如何定位问题? A:能够尝试在Asset Studio中查看相干AssetBundle的具体资源。 个别AssetBundle中的资源都是已命名的,检测到的未命名的资源通常是脚本生成的Asset中的一部分。 能够用Asset Studio查看这些未命名资源和哪些Asset相干,进而加以定位。 感激宗卉轩@UWA问答社区提供了答复 封面图来源于网络 明天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题兴许都只是冰山一角,咱们早已在UWA问答网站上筹备了更多的技术话题等你一起来摸索和分享。欢送酷爱提高的你退出,兴许你的办法恰能解他人的当务之急;而他山之“石”,也能攻你之“玉”。 官网:www.uwa4d.com官网技术博客:blog.uwa4d.com官网问答社区:answer.uwa4d.comUWA学堂:edu.uwa4d.com官网技术QQ群:793972859(原群已满员)

May 25, 2022 · 1 min · jiezi

关于ui:OneOSLite-上提升-LVGL-帧率的方法

1.前言LVGL的移植是第一步,这使得OneOS-Lite上领有了图形界面的能力。下一步,则是如何晋升LVGL帧率问题。总结了一下,大略有以下几个方面。帧率FPS:FPS是图像畛域中的定义,是指画面每秒传输帧数,艰深来讲就是指动画或视频的画面数。FPS是测量用于保留、显示动静视频的信息数量。每秒钟帧数越多,所显示的动作就会越晦涩。通常,要防止动作不晦涩的最低是30。 硬件自身性能OneOS-Lite零碎影响LVGL2.硬件自身性能通常,要使得FPS更高,硬件选型是第一步,至关重要的一步。 内核,解决能力越强,对FPS会有晋升。内存,尽量抉择SRAM,对FPS晋升很大,SDRAM相比于SRAM逊色不少。传输方式SPI/LCD/DSI,应用SPI传输缓存数据至屏幕,显然不如LCD或者DSI。具备专门解决图形图像的硬件,比方stm32的DMA2D。更小的屏幕(分辨率)。3.OneOS-Lite零碎影响LVGL自身是运行在OneOS-Lite之上的,因而,OneOS-Lite的配置会对帧率产生影响。硬件的反对也须要零碎的治理。 tick frequecy设置低一些,可能会进步帧率。想一想也是哈,tick frequecy影响的是时钟中断。设置低一些,时钟中断会来得没有那么频繁。尽量应用SRAM。即便同样是SRAM,应用全局变量会比应用malloc调配,取得更高的帧率。想一想也是哈,内存治理须要耗费工夫。OneOS-Lite反对LTDC,DSI,DMA2D等不要让lvgl优先级太低,如果更高优先级的工作频繁执行,会影响图形显示性能。尽量让存储帧缓冲器的存储器仅用于帧缓冲,如果用于存储帧缓冲器的存储器还用于其余利用,那可能会影响零碎的图形性能。4. LVGLLVGL自身的配置也是影响其性能。 不要关上性能监控LV_USE_PERF_MONITOR && LV_USE_MEM_MONITOR如果反对,倡议开启LV_USE_GPU_STM32_DMA2D倡议帧缓存区不要低于屏幕的1/4,倡议双缓存5.其它应用更高的优化级别,能进步帧率。 6.关注&&分割开源轻量操作系统: https://gitee.com/cmcc-oneos/OneOS-Lite docs文档核心: https://oneos-lite.com/ 知知乎乎:蓁蓁

May 13, 2022 · 1 min · jiezi

关于ui:NGUI-Label-自定义材质球无效

1)NGUI Label 自定义材质球有效2)代码保留预制体呈现图片失落的问题3)降级Unity版本,粒子系统触发的闪退问题4)场景打包AssetBundle过大 这是第296篇UWA技术常识分享的推送。明天咱们持续为大家精选了若干和开发、优化相干的问题,倡议浏览工夫10分钟,认真读完必有播种。 UWA 问答社区:answer.uwa4d.comUWA QQ群2:793972859(原群已满员) NGUIQ:想在NGUI下做一个字体溶解Shader,自定义的Shader材质球给Label不起作用,有没有大佬理解这块内容? A:猜想题主是要在编辑器外面的材质球对象上调整_Threshold的数值,但在Game窗口发现文本没有发生变化。 实质起因是NGUI在对Label进行渲染的时候应用的并不是编辑器外面赋值的材质球,而是在NGUI进行合并DrawCall后动态创建的Material,所以咱们须要对这个材质球进行材质球属性设置。 这里能够通过脚本来给理论渲染Label的材质球调整属性达到成果。以下别离是Threshold为0和Threshold为0.4的成果。 public class TestLabel : MonoBehaviour{ public float threshold; public UILabel label; void Update() { if (label.drawCall != null) label.drawCall.dynamicMaterial.SetFloat("_Threshold", threshold); }}PS:这样解决的害处是,和这个Label在同一个DrawCall的Label都会受到影响,所以须要将这些成果的Label的Depth做非凡解决,和其它的Label不放在同一个DrawCall中。 另外在NGUI的UI DrawCall脚本中,能够关上SHOW_HIDDEN_OBJECTS,这样在编辑器外面是能够看到生成具体的DrawCall对象,也就能够看到它们的材质球属性变动。 从下图能够看到具体的DrawCall,它的材质球名字会在后面加[NGUI]的字样,和编辑器里不是同一个材质球。 感激Xuan@UWA问答社区提供了答复 ScriptQ:我在Unity 2021.3.x上想依据配置动静生成预制体。当初遇到的问题是,能够把图片动静读取下来,而后保留预制体了当前,图片就失落了,应该是须要批改.prefab里的值 。 我应用SerializedObject模块.objectReferenceValue去批改m_Sprite不会失效,它是援用类型的,而值类型的都是能够批改的。有大佬晓得怎么批改嘛? A:用上面的代码,是能够在编辑器里改的,不太确定楼主是不是想要在编辑器里操作,还是在Runtime下操作,实践上Runtime时是没有预制体的概念的。在编辑器外面,选中预制体,而后点击Update Prefab Asset按钮即可。 预制体很简略: public class Example{ [MenuItem("Examples/Update Prefab Asset")] static void AddBoxColliderToPrefab() { // Get the Prefab Asset root GameObject and its asset path. GameObject assetRoot = Selection.activeObject as GameObject; string assetPath = AssetDatabase.GetAssetPath(assetRoot); Texture2D tt = AssetDatabase.LoadAssetAtPath("Assets/2.jpg", typeof(Texture2D)) as Texture2D; Sprite ss = AssetDatabase.LoadAssetAtPath("Assets/3.jpg", typeof(Sprite)) as Sprite; // Load the contents of the Prefab Asset. GameObject contentsRoot = PrefabUtility.LoadPrefabContents(assetPath); // Modify Prefab contents. contentsRoot.transform.Find("RawImage").GetComponent<UnityEngine.UI.RawImage>().texture = tt; contentsRoot.transform.Find("Image").GetComponent<UnityEngine.UI.Image>().sprite = ss; contentsRoot.AddComponent<BoxCollider>(); // Save contents back to Prefab Asset and unload contents. PrefabUtility.SaveAsPrefabAsset(contentsRoot, assetPath); PrefabUtility.UnloadPrefabContents(contentsRoot); }}感激Xuan@UWA问答社区提供了答复 ...

May 12, 2022 · 1 min · jiezi

关于ui:AssetBundle加载的TMP字体材质赋值失败

1)AssetBundle加载的TMP字体材质赋值失败2)资源打包关系依赖树3)Shader中设置ColorMask后,最终输入色彩的计算4)Time.deltaTime和Time.unscaledDeltaTime值不统一 这是第293篇UWA技术常识分享的推送。明天咱们持续为大家精选了若干和开发、优化相干的问题,倡议浏览工夫10分钟,认真读完必有播种。 UWA 问答社区:answer.uwa4d.comUWA QQ群2:793972859(原群已满员) UGUIQ:从AssetBundle加载的TMP字体材质,为什么在代码中设置为TMP组件的材质会失败? 应用Resources.Load加载材质赋值是能够的,然而AssetBundle加载代码赋材质却行不通。这里是把材质和字体都打了AssetBundle,TMP相干其余的没有打AssetBundle,走的TMP自身的Resources.Load加载形式。 从AssetBundle中LoadAsset后用一变量存了材质,而后所有text的材质赋值都拜访了这个变量。 A:TMP字体外部默认用的Resources.Load的形式加载资源,倡议批改TMP源码,把所有Resources.Load加载资源的形式代理成本人的。 咱们的计划是:在非AssetBundle模式下用它本身的加载形式。AssetBundle模式下,就会由框架的资源加载代理。 感激1 9 7 3-311135@UWA问答社区提供了答复 AssetBundleQ:想做包体资源剖析,大家有什么好的树显示工具或者思路举荐吗?有比拟好的开源计划也能够。最简略就像N叉树一样,比方root一个文件名,而后开展整个树结构。 A1:我本人做了一个,供参考。都是用Unity本人的IMGUI最根本的接口去实现。EditorWindowsGUI.BoxGUI.BeginGroupGUI.LabelHandles.DrawBezierHandles.DrawWireDiscTreeView基本上,组织好各个AssetBundle的依赖关系其实是很好出现的。感激黄程@UWA问答社区提供了答复 A2:举荐一款比拟好用的插件,不止有依赖树,还有其余打包的资源数据可供剖析:https://assetstore.unity.com/... 感激郑骁@UWA问答社区提供了答复* ShaderQ:场景中有一个相机,ClearFlags为SolidColor,色彩设置为Blue(0,0,1,1)。 另外创立了个Cube,应用 一个最一般的vf shader,设置的输入色彩是Red,即fixed4(1,0,0,1);应用默认的ColorMask设置则会输入红色Cube,然而若设置了ColorMask R,最终Cube的色彩显示的是Blue和Red混合进去的,这一块有什么文档能够参考吗? 附Shader: Shader "Custom/ColorMask"{ Properties { } SubShader { Tags { "RenderType"="Opaque" } Pass { ColorMask R CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; }; struct v2f { float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { return fixed4(1, 0, 0, 1); } ENDCG } }}A1:猜想ColorMask R应该是只写入以后R通道的色彩,然而GB依然会从摄像机的Background值获取。感激Knight-132872@UWA问答社区提供了答复 ...

April 25, 2022 · 1 min · jiezi

关于ui:虚幻引擎-5-来了不止-LumenNanite-新技术性能及-UI-均迎来大升级

4 月 5 日,Epic Games(以下简称“Epic”)创始人兼 CEO Tim Sweeney 在空幻引擎“State Of Unreal”直播流动上正式公布了空幻引擎 5(Unreal Engine 5) —— “咱们十分冲动地发表,当初能够下载空幻引擎 5 了”。 此次 空幻引擎 5 的到来,将在实时 3D 内容和体验方面给宽广用户玩家们带来前所未有的自由度、逼真度和灵活性,同时推动大小型团队都能以视觉和交互的形式真正冲破可能的界线。 空幻引擎 5 采纳了全新新技术设计,如“全动静全局光照解决方案”Lumen、“虚拟化多边形几何体零碎” Nanite,性能和 UI 方面也有了大量降级,可实现更加真切的视觉效果。 只管 Lumen 和 Nanite 等一些次要新性能尚未在非游戏工作流中失去验证(这是将来版本的一个继续指标),但所有创作者都将可能持续应用 UE 4.27 反对的工作流。但他们也将受害于从新设计的空幻编辑器、更好的性能、对艺术家敌对的动画工具、扩大的网格创立和编辑工具集、改良的门路跟踪等等。 空幻引擎 5 次要新性能下一代实时渲染空幻引擎 5 推出了一系列开创性的性能,可实现高保真细节出现的实时世界。 Lumen:这是一个齐全动静的全局照明解决方案,它可让用户玩家可能创立可信的场景,其中间接照明能够动静适应直接照明或几何体的变动,如随着工夫扭转太阳的角度、关上手电筒或关上外门。 通过应用 Lumen,你无需再编写光照贴图 UV、期待光照贴图烘焙或搁置反射捕获;只需在虚构编辑器中创立和编辑灯光,就能看到玩家在指标平台上运行游戏或体验时看到的最终灯光。 Nanite:新的虚拟化微多边形几何零碎,让你可能用大量的几何细节发明游戏和体验。间接导入由数百万个多边形组成的电影品质源艺术,从 ZBrush 雕刻到摄影测量扫描,并将其搁置数百万次,同时放弃实时帧速率,且不会呈现任何显著的保真度损失。 虚构暗影贴图(VSM):专为 Lumen 和 Nanite 需要设计,可提供正当、可控的性能老本和正当的软暗影。Nanite 和 VSM 只智能地流式解决您能够感知的细节,在很大水平上打消了多边形计数和绘制调用束缚,并打消了耗时的工作,如将细节烘焙到法线贴图和手动编写 LOD,从而让您可能专一于创造力。 对于现在的宽广玩家来说,高分辨率显示器上的帧速率为 60 fps 或更高成了根本需要,这给渲染资源带来了微小的压力。应用内置的、独立于平台的高质量上采样系统 Temporal Super Resolution(TSR),引擎不仅能以更低的分辨率渲染,同时还能与以更高分辨率渲染的帧相似的输入像素保真度。 新凋谢世界工具集正如创始人兼 CEO Tim Sweeney 在公布流动上示意的那样:“让所有规模的团队更快、更容易、更具协作性地创立凋谢世界”。空幻引擎 5 的到来,给新世界分区零碎扭转了级别的治理和流化形式,主动将世界划分为网格,并流化必要的单元。 ...

April 7, 2022 · 1 min · jiezi

关于ui:UIToolkit下一代UI系统

1. UIToolkit运行时――下一代UI零碎UIToolkit的前身是UIElement,公布于Unity 2018。起初它用于开发Editor编辑面板中的UI,自Unity 2019、Unity 2020起正式反对运行时UI并且更名为UIToolkit,它以Package包的模式存在。自Unity 2021.2起,UIToolkit被官网内置在Unity中和UGUI的位置统一,UIToolkit作为下一代UI零碎,设计之初指标就很明确,就是替换掉现有的UGUI零碎。 现有的UGUI零碎从2014年自Unity 4.6开始至今服务于太多我的项目,从我的角度来看UGUI应用上并没有太大问题,最大的问题就是效率低。我用UGUI也开发了好几款我的项目了,每个我的项目开发阶段都被吐槽UI关上慢、卡顿等等,导致咱们不得不花很多工夫去优化UI零碎。 可循环滑动列表、图文混排、正当的UI划分还有资源按需加载基本上每个我的项目都要做一遍,尤其是滑动列表只有Item数量超过50个必卡无疑,导致每个我的项目每家公司都要本人做一遍,然而UIToolkit官网就内置了循环列表的性能。UGUI为什么效率低呢?就拿滑动列表来说吧,超出显示区域以外的面也须要合并Mesh,VBO数据也须要给GPU传,这样元素多提交GPU的数据量就大,还有就是它基于GameObject的形式,导致必然须要加载大量没用的数据,参加序列化的脚本数据有用没用都须要加载,导致简直没有任何缓存命中率可言。 GameObject的形式以致UI效率低,Unity开发的DOTS就是为了解决GameObject的问题,对DOTS感兴趣的敌人欢送看我之前的课程《DOTS深度钻研之原理剖析篇》和《DOTS深度钻研之利用实际篇》。UGUI还有那几个LayoutGroup、ContentSizefitter组件元素只有多一点就容易卡顿。UIToolkit继承了UGUI做得好的中央,比方Mesh合并就被保留下来,而且内置的ListView和ScrollView自带了有限循环列表的性能,并不会将所有看不见的元素都进行合并与提交GPU。它并没有采纳GameObject的形式,参考了Web技术的XML和CSS计划,只保留变动的数据。 期初在UGUI诞生前甚至在NGUI诞生前Unity本人是有一套UI零碎的,称之为IMGUI。它的运行原理更恐怖,每帧无论UI是否变动都须要将VB/IB传入GPU中,每个UI元素都要每帧设置渲染状态,DrawCall无疑就会十分恐怖。它在编辑模式下和运行时都同时反对,因为编辑模式下对性能并没有要求,而且编辑面板也不会制作特地简单的性能,所以IMGUI被广泛应用于拓展编辑器的开发。而运行时的OnGUI简直没有我的项目应用,因为应用不便当(没有编辑器),效率低(DrawCall高)的起因,所以起初在AssetStore中诞生了NGUI这样的第三方UI零碎。随后在Unity 4.6中Unity官网也开发出UGUI。UIToolkit的诞生是有时代意义的,同时兼顾了编辑模式与运行模式的布局便利性与性能,保障同一套代码资源能够疾速移植。UIToolkit目前处于高速倒退中,论坛中开发者探讨的十分强烈,版本迭代的速度也十分快,通过UIToolkit被内置在引擎中足以见得官网对UIToolkit的应用信心。 很多敌人看到XML和CSS就联想到UIToolkit应用了上世纪90年代的老技术,还和WPF和WinForm作比拟。就我看来XML和CSS只是界面的形容信息,就算是UGUI用的也是YAML来保留的界面形容信息,真正外围的技术应该是如何渲染与优化才对,如下图所示,UIToolkit中不同图集、不同文字、图文混排和不同深度只须要1次DrawCall就能够画完。 UIToolkit在GPU保留了一份以后UI渲染的VB/IB,当渲染的UI发生变化时它会以最小的代价来更新这份VB/IB,它不会更新全副的VB/IB,只更新变动的局部保障CPU和GPU每帧都能高度运行。为了一次DrawCall能画完,它还实现了一套大而全的Ubershader,同时反对8个图集和1个字体,只有界面满足以上条件就能够一次DrawCall画完,如果对UI原理感兴趣的敌人在文章的前面有具体的介绍。 而且UIToolkit应用的UXML只记录了变动的数据,比方大部分图片在UI中可能永远都不会设置色彩、旋转或缩放。UIToolkit提供了默认参数,只有不批改它,UXML中就不会蕴含额定数据。反观UGUI它无论如何都须要把色彩、旋转和缩放一类的数据序列化在脚本中,一个简单的界面可能有500多个元素,然而大量的内存都被白白浪费了,毫无缓存命中率可言,从这一点来看UIToolkit也要比UGUI先进。 1.1 致敬首先来致敬一下UIToolkit的两位次要开发者Benoit Dupuis(贝努瓦·杜普伊)和 Damian Campeanu(达米安·坎皮努)。感激在我学习的过程中在unite和官方论坛中屡次看到他们精彩的分享与急躁的解答。贝努瓦·杜普伊(左) 达米安·坎皮努(右)(图片来自领英) 贝努瓦·杜普伊在论坛中屡次公布UIToolkit的最新技术栈,以及UIToolkit将来的打算Roadmap解答开发者的疑难。达米安·坎皮努是UIToolkit的次要开发者,而且他还是DOTS编辑器团队的老大。 UIToolkit官方论坛:https://forum.unity.com/threads/ui-update-q3-2021.1138603/ 1.2 下一代UI零碎Unity在2018年公布UIElement时我就简略看过,因为过后它只反对Editor面板的UI,所以我就没有持续深刻学习。Unity在2019年UIElement改名UIToolkit并且反对运行时UI时我又简略学了一遍,发现它就是基于XML和CSS的20年前的老技术,期初心田是有点抵制的,直到Unity 2021.2正式内置UIToolkit到引擎中,又一次勾起了我学习它的激情。时至今日它仍然有很多问题是无奈解决的,如:UI和粒子特效叠层、UI和3D模型叠层、UI裁剪粒子特效、3D界面、多UI相机叠层、UI非凡着色器和K帧动画等等。 毕竟UGUI曾经好几年都没更新了,一个好引擎必然要跟上时代,替用户解决通用的痛点问题。在我看了UIToolkit最新的Roadmap当前我更加深信UIToolkit会代替UGUI。目前无奈满足需要的中央官网正在踊跃的开发,UIToolkit曾经反对UGUI混合应用、反对Text mesh pro 图文混排和有限滑动列表。将来会反对Shader Graph、3D粒子与UI的叠层和深度问题。大家如果有需要也能够在这个链接中公布本人的想法,这里也能看到官网正在反对以及将来反对的性能。 https://unity.com/roadmap/unity-platform/gameplay-ui-design 1.3 装置UIToolkit最好应用Unity 2021.2以及以前的版本,此版本中UIToolkit曾经内置在引擎中不须要增加额定的Package包。毛病就是不利于查看C#源码,它将DLL内置在引擎中。如果对源码有趣味的敌人也能够应用Unity 2020.3以及以上版本,但须要手动装置Package包。目前有个别性能只有在Unity 2021.2中才有,所以本教程我采纳最新Unity 2021.2.9f1版本,在源码学习局部我会切到Unity 2020.3版本。 如果是Unity 2020版本,如下图所示,须要在PackageManager中增加,com.unity.ui和com.unity.ui.builder两个包,目前两个包的最新版本都是1.0.0-preview.18版本。 "com.unity.ui": "1.0.0-preview.18","com.unity.ui.builder": "1.0.0-preview.18", com.unity.ui包就是UIToolkit,com.unity.ui.builder包则是UIBuilder,UIBuilder是UXML和USS的可视化编辑器,让使用者不须要手写布局代码,在编辑器中能够不便做出各式各样的界面。 戳此《UIToolkit下一代UI零碎》查看全文。

March 2, 2022 · 1 min · jiezi

关于ui:虎年首发尚硅谷AI案例实战教程分享

AI(Adobe illustrator)是寰球驰名的矢量绘图软件,以其弱小的性能和体贴用户的界面,占据了寰球矢量绘图软件的大部分份额。AI次要利用于印刷出版、海报书籍排版、业余插画、多媒体图像处理和互联网页面的制作等,也能够为线稿提供较高的精度和管制,适宜生产任何小型设计到大型的简单我的项目。 本套视频教程包含六大部分:AI根底软件操作、图形绘制、字体设计、插画设计工具根底、AI软件晋升实战、版式设计,是一套残缺的AI软件基础教程,简略、易学、好动手。通过本套教程的学习,你将把握AI软件的纯熟操作,为艺术创作夯实根底,加之学习了版式设计技巧和各类排版设计法令,能够独立实现精美的画面创作。 即使你是小有工作教训的设计师,通过本套教程也可坚固AI新版软件的性能,进步工作效率,丰盛设计成果。通过版式设计的学习,无论是零根底小白,还是工作教训1-3年的设计师,都能够晋升设计成果的思路和技巧。 小贴士:AI和PS在兼容上浑然一体,二者的区别简略来说,PS通常被认为是一个图像编辑软件,是一个以解决栅格图片为主的像素图处理软件(图片放大后会糊);而AI通常被认为是一个图像创作软件,是一个以矢量图的绘制为主的矢量图创作软件(有限放大也不会糊)。 教程具体目录: 01.AI实战-教程简介02.AI实战-新建和关上03.AI实战-色彩模式与出血04.AI实战-文件存储与文件格式05.AI实战-抓手放大镜标尺06.AI实战-抉择工具间接抉择工具撤销07.AI实战-矩形圆角矩形填充描边08.AI实战-椭圆工具多边形工具09.AI实战-图层程序锁定原位复制10.AI实战-图层属性11.AI实战-魔术棒套索工具12.AI实战-钢笔工具13.AI实战-字体工具段落文字和转门路14.AI实战-字体工具文字盘绕区域文字15.AI实战-字体工具直排文字润饰和变形选项16.AI实战-字体工具字符菜单17.AI实战-字体工具段落菜单18.AI实战-直线工具19.AI实战-画笔铅笔工具20.AI实战-剪刀橡皮工具21.AI实战-旋转镜像工具22.AI实战-歪斜工具23.AI实战-变形工具24.AI实战-自在变形工具25.AI实战-门路查找器形态模式26.AI实战-门路查找器简单图形运算27.AI实战-实时上色28.AI实战-透视网格工具29.AI实战-突变工具30.AI实战-网格工具31.AI实战-吸管工具属性32.AI实战-混合工具33.AI实战-喷枪工具34.AI实战-表格工具合集35.AI实战-对齐与参考线36.AI实战-变换菜单37.AI实战-剪切蒙版38.AI实战-图像描摹毛笔字设计39.AI实战-英文平面字成果40.AI实战-透明度蒙版41.AI实战-描边属性42.AI实战-多重描边43.AI实战-插图图标绘制44.AI实战-插图海报制作45.AI实战-半透明标记46.AI实战-花朵成果47.AI实战-卡通形象制作48.AI实战-3D突出与斜角49.AI实战-2.5D插画50.AI实战-字体排版与版权51.AI实战-英文字体设计52.AI实战-中文字体设计53.AI实战-版式设计54.AI实战-黄金比例尺55.AI实战-版式六大准则56.AI实战-色彩搭配57.AI实战-banner设计58.AI实战-banner的布局59.AI实战-banner的宰割60.AI实战-banner的正当与禁区61.AI实战-banner的背景设计

February 10, 2022 · 1 min · jiezi

关于ui:使用Sprite-Packer对UI图集进行打包的问题

1)应用Sprite Packer对UI图集进行打包的问题2)Unity解决视锥剔除的问题3)Profiler查看Shader.CreateGPUProgram的具体Shader信息4)PPS的UberPost内存占用较高 这是第270篇UWA技术常识分享的推送。明天咱们持续为大家精选了若干和开发、优化相干的问题,倡议浏览工夫10分钟,认真读完必有播种。 UWA 问答社区:answer.uwa4d.comUWA QQ群2:793972859(原群已满员) UIQ:应用Sprite Packer对UI图集进行打包:1. 应用零碎自带的Sprite Packer打包,图集大小设置成1024,这样打进去的图集会分为好几个Group,那么雷同图集不同Group可能合批吗? 2. 因为Sprite Packer是主动调配的Group,这种状况下关上性能的时候是所有Group都会加载的,那么这种设置1024是否还有必要,改成2048会不会好一点? 3. 图集设置成2048变大了,对加载速度和内存有影响吗?这个须要按图集里图片数量多少做不同的设置吗? A:能够参考以下答案: 不同Group是不会合批的,个别也没有UI图集大小的倡议。最好本人手动管制图集的Group,在UI制作时能够以UI Prefab为单位收集一下哪些Group的图集是以后UI须要应用的,加载UI预设时按需加载对应的Group就好了。如果图集中的图片资源都用到了,同时图集的填充率也差不多,那加载1张2048和4张1024,对加载影响可能并不是很显著,内存占用也是一样的,但如果2048填充率并没那么高,或者1024的会比拟好。该答复由UWA提供 RenderingQ:最近在钻研场景治理的问题,想理解Unity是否有解决视锥剔除,于是做了测试(Unity 2020.3.11f)。创立了两个Cube,挪动第二个Cube,比照顶点数量变动。 为什么同样在视锥体范畴内,批次和顶点数会不同?当第二个Cube不在视锥范畴内,顶点数也可能不同,那Unity是否有视锥剔除,其解决逻辑是怎么样的? A:应该是灯光绘制ShaowMap时候,投影的范畴将那个相机外的Cube包进去了,受Light的影响,关了Light就能够了。感激范世青@UWA问答社区提供了答复 ShaderQ:因为目前我的项目Shader挺简单的,变体也十分的多,如果用ShaderVariantCollection的形式解析一次耗时太高了,而且如果用Shader.CreateGPUProgram耗时很高并且频率也很高,所以就想问下:有没有什么方法能够晓得是哪个Shader造成的?这样我才好做策略去解决。 我以前Unity 2017是可能晓得哪个Shader造成的,在Profiler外面能够把Shader.CreateGPUProgram持续开展,然而当初Unity 2018.4.36不行。 A:Profiler的Timeline视图能够看到。感激黄晓文@UWA问答社区提供了答复 ShaderQ:URP我的项目,上面这个Shader是做什么用的?有没有相干的变体算法和优化倡议,谢谢。 A:UberPost是一个大杂烩,有好多解决都集中在这里,我的项目确定不必的变体之后,就能够用#pragma skip_variants xxx 跳过。例如:#pragma skip_variants _TONEMAP_NEUTRAL _DITHERING。感激怀特@UWA问答社区提供了答复 20211011更多精彩问题等你答复~ Unity增量打包AssetBundle没变动的资源也会被从新打包在模型有UV2的状况下开启Generate Lightmap UVs如何实现AAB包的增量更新封面图来源于网络 明天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题兴许都只是冰山一角,咱们早已在UWA问答网站上筹备了更多的技术话题等你一起来摸索和分享。欢送酷爱提高的你退出,兴许你的办法恰能解他人的当务之急;而他山之“石”,也能攻你之“玉”。 官网:www.uwa4d.com官网技术博客:blog.uwa4d.com官网问答社区:answer.uwa4d.comUWA学堂:edu.uwa4d.com官网技术QQ群:793972859(原群已满员)

October 13, 2021 · 1 min · jiezi

关于ui:如何修复-SAP-UI5-aggregation-with-cardinality-01-相关的错误消息

谬误音讯: Assertion failed: multiple aggregates defined for aggregation with cardinality 0..1 引起该谬误的 aggregation 名称:flexContent: 查看 xml 视图里该 aggregation,发现其下定义了两个控件: 删除任意一个即可: 如上图所示,我将 Popover 和 VizFrame 从新搁置,别离位于不同的 aggregation 里,之后问题隐没。 残缺的 xml 视图代码: <mvc:View controllerName="sap.viz.sample.Line.Line" xmlns="sap.m" xmlns:viz="sap.viz.ui5.controls" xmlns:layout="sap.ui.layout" xmlns:mvc="sap.ui.core.mvc" xmlns:viz.feeds="sap.viz.ui5.controls.common.feeds" xmlns:viz.data="sap.viz.ui5.data" height="100%"> <layout:FixFlex id='chartFixFlex' minFlexSize="250"> <layout:fixContent> <viz:Popover id="idPopOver"></viz:Popover> </layout:fixContent> <layout:flexContent> <viz:VizFrame id="jerryFrame" uiConfig="{applicationSet:'fiori'}" height='100%' width="100%" vizType='line'> <viz:dataset> <viz.data:FlattenedDataset data="{/milk}"> <viz.data:dimensions> <viz.data:DimensionDefinition name="Week" value="{Week}" /> </viz.data:dimensions> <viz.data:measures> <viz.data:MeasureDefinition name="Revenue" value="{Revenue}" /> <viz.data:MeasureDefinition name="Cost" value="{Cost}" /> </viz.data:measures> </viz.data:FlattenedDataset> </viz:dataset> <viz:feeds> <viz.feeds:FeedItem id='valueAxisFeed' uid="valueAxis" type="Measure" values="Revenue" /> <viz.feeds:FeedItem id='valueAxisFeed2' uid="valueAxis" type="Measure" values="Cost" /> <viz.feeds:FeedItem id='categoryAxisFeed' uid="categoryAxis" type="Dimension" values="Week" /> </viz:feeds> </viz:VizFrame> </layout:flexContent> </layout:FixFlex></mvc:View>更多Jerry的原创文章,尽在:"汪子熙":

October 2, 2021 · 1 min · jiezi

关于ui:背包优化问题

1)背包优化问题2)Unity 2019在华为手机上2倍抗锯齿不失效3)对于libunity.sym.so符号表的问题4)Navmesh合并成一个新的NavMesh的办法5)Prefab挂本人写的管理器脚本却无奈打包 这是第260篇UWA技术常识分享的推送。明天咱们持续为大家精选了若干和开发、优化相干的问题,倡议浏览工夫10分钟,认真读完必有播种。 UWA 问答社区:answer.uwa4d.comUWA QQ群2:793972859(原群已满员) UGUIQ:常常会有人问背包卡顿的起因剖析,那到底是有多少种卡顿的起因以及优化的形式呢? A1:举荐先看一下官网的UGUI课程:《Unity UI模块优化案例精讲》对于没有优化过的背包,最大的耗费在于物品的销毁与实例化。感激潜行医生-577493@UWA问答社区提供了答复 A2:以下是我想到的一些点,期待其余大神补充更多的内容。我的项目中理论遇到的具体问题还须要具体分析,通过性能工具进行定位耗费过高的中央,而后有针对性地进行正当优化。 1.对于未优化的背包,列表滚动过程中,Cell的加载和销毁会造成卡顿。解决办法:应用有限滚动列表Tableview这种控件,使Cell能够进行复用,防止物体的频繁实例化和销毁。 2.背包关上一瞬间,加载了很多个格子物品进来,造成卡顿和内存冲高,GC等问题。解决办法:分帧加载,管制一帧实例化的Cell个数,防止在同一帧内大量创立格子物品。针对资源Load过程中的卡顿,能够采纳提前预加载资源到内存的形式。 3.背包刷新时卡顿,大量的格子监听某个事件,刷新时可能造成卡顿。解决办法:用脏标记的形式去刷新,不该刷新的局部不刷新。 4.Cell上的图片,粒子特效显示等设计不合理造成DrawCall过高。解决办法:这个须要联合具体的我的项目和设计方案进行剖析和优化。 感激马三小伙儿@UWA问答社区提供了答复 A3:举荐一下FairyGUI这套UI零碎,FairyGUI的List只有SetVirtual就行了:https://www.fairygui.com/docs...感激萧小俊@UWA问答社区提供了答复,欢送大家转至社区交换: RenderingQ:Unity 2019上,工程同时应用PostProcessing后处理性能加上抗锯齿,在华为手机上2倍抗锯齿是没成果的。 开启后处理+抗锯齿在手机上的效果图: 敞开后处理+抗锯齿在手机上的效果图: A:测试机型光荣Play 4T在开启后处理的状况下,2x MSAA不会失效,然而4x和8x会失效。 应用Graphics Analyzer剖析真机上的gl调用发现,MSAA失效须要在场景加载前调用 glFramebufferTexture2DMultisampleEXT接口,然而在开启后处理性能并且设置为2x MSAA的状况下,引擎调用的却是glFramebufferTexture2D接口,导致抗锯齿在场景中没有失效。 然而测试小米8和Oppo A32在开启后处理的状况下设置为2x MSAA并且失效。猜想是引擎方面针对不同的GPU硬件做了一些设置,当初只能在性能容许的状况下尝试开启4x MSAA来达到抗锯齿成果。 附件(可戳原问答下载)中有两个测试APK包和一些真机测试后果。光荣Play 4T上不开启后处理的状况下,单纯的2x MSAA和4x成果简直截然不同。 感激宗卉轩@UWA问答社区提供了答复,欢送大家转至社区交换: AndroidQ:我的项目采纳的是IL2CPP模式打包,选取arm64-v8a和armeabi-v7a两个版本,没有开启Strip Engine Code选项,Unity打包版本是2018.3.6f1 当初线上的包有ANR报错如下: #00 pc 000000000004b7cc /apex/com.android.runtime/lib64/bionic/libc.so (syscall+28) #00 pc 000000000004f680 /apex/com.android.runtime/lib64/bionic/libc.so (__futex_wait_ex(void volatile*, bool, int, bool, timespec const*)+144) #00 pc 00000000000b2120 /apex/com.android.runtime/lib64/bionic/libc.so (NonPI::MutexLockWithTimeout(pthread_mutex_internal_t*, bool, timespec const*)+688) #00 pc 00000000009fe7b4 /data/app/~~AxXJ-H5OqAWLfQK2almqqg==/com.saiyun.avgchapters.episodestories.romance-HeZPf9aJrCJYoKrQ20DOrg==/lib/arm64/libunity.so (???) #00 pc 00000000009fe778 /data/app/~~AxXJ-H5OqAWLfQK2almqqg==/com.saiyun.avgchapters.episodestories.romance-HeZPf9aJrCJYoKrQ20DOrg==/lib/arm64/libunity.so (???) #00 pc 0000000000508938 /data/app/~~AxXJ-H5OqAWLfQK2almqqg==/com.saiyun.avgchapters.episodestories.romance-HeZPf9aJrCJYoKrQ20DOrg==/lib/arm64/libunity.so (???) #00 pc 0000000000347f1c /data/app/~~AxXJ-H5OqAWLfQK2almqqg==/com.saiyun.avgchapters.episodestories.romance-HeZPf9aJrCJYoKrQ20DOrg==/lib/arm64/libunity.so (???) #00 pc 00000000006e3638 /data/app/~~AxXJ-H5OqAWLfQK2almqqg==/com.saiyun.avgchapters.episodestories.romance-HeZPf9aJrCJYoKrQ20DOrg==/lib/arm64/libunity.so (???) at com.unity3d.player.UnityPlayer.nativeInjectEvent (Native method) at com.unity3d.player.UnityPlayer.injectEvent (unavailable) at com.unity3d.player.UnityPlayer.onTouchEvent (unavailable) at android.view.View.dispatchTouchEvent (View.java:15199) at android.view.ViewGroup.dispatchTransformedTouchEvent (ViewGroup.java:3914) at android.view.ViewGroup.dispatchTouchEvent (ViewGroup.java:3578)查看报错的机型Samsung Galaxy S10,应用的是arm64运行的游戏,我尝试应用PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Symbols\arm64-v8a下的libunity.sym.so文件查找函数名,然而提醒找不到办法。 ...

July 28, 2021 · 1 min · jiezi

关于ui:UI设计是什么UI设计学什么

UI设计是什么? UI是User Interface的缩写,中文的意思是用户界面,再说直观一点就是,比方你在电脑下面或者手机下面能用眼睛看见都能够叫用户界面。UI设计其实是一个大通称,外面细化了有界面设计,怎么把界面弄特炫酷、难看;交互设计,人机交互,设计一套流程下来,让软件更易用,更容易了解,让计算机不便人的应用;用户体验设计,怎么把这个界面设计得更容易操作,更合乎逻辑,缩小用户记忆和学习的累赘。 下面的这些职位划分,只有真正的大公司才会分得这么细,更多的公司是一名UI设计师干多种职位的事件,当初社会须要的是复合型的人才,所以咱们学UI也是要很多货色的。 UI设计是学什么? 参考易牛云朗沃的UI课程安顿,咱们学UI设计最根底的就是要会设计图片,像PS软件这种对图片解决的软件是要学习的;之后须要学习业余内容页面设计的学习,比方游戏网站设计,电商网站界面设计等等;而后就须要进一步学习挪动端的界面设计了,像APP界面设计,图标设计等等;下一步就要学习一些逻辑方面的常识,与计算机的交互设计,用户体验设计,如何达到让用户上手就会,爱不释手;最初也是须要学习一些代码的,个别就是HTML、CSS、JS等的学习,UI设计是在工作中免不了和前端工程师打交道,懂代码是很有必要的。 一名杰出的UI设计,私下还会去看看无关心理学方面的常识,这样能更好的了解用户行为,做出更人性化的操作界面,就拿咱们大家都晓得QQ来说吧,你的一个群外面十分沉闷,关上就是99+的音讯,那个包裹数字的小红点删除的形式是怎么?用手指一擦就拿掉了,是不是就像在咱们平时在桌上看见一个小黑点,会用手指去擦的操作是一样的?这就是UI设计师须要做的事。 UI设计是什么?UI设计学什么?置信通过我下面的介绍大家应该有个初步的理解了,在将来人们会更加器重精力上的享受,这给UI设计提供了良好的发展前途。

June 29, 2021 · 1 min · jiezi

关于ui:如何在-SAP-UI5-应用中集成第三方库-一个在移动设备上查看-Web-应用打印调试信息的小技巧

这是 Jerry 2021 年的第 43 篇文章,也是汪子熙公众号总共第 320 篇原创文章。 做 Web 开发的程序员,无论应用 SAP UI5,还是 Angular,React,Vue,每天都离不开 Chrome / Firefox 开发者工具。 Jerry 2018 年的时候,已经写过一篇对于 Chrome 开发者工具的文章:Jerry 和您聊聊 Chrome 开发者工具。 这些开发者工具尽管好用,然而当 Web 利用在挪动设施上运行时,想间接在手机浏览器上查看其应用 console.log 打印出的日志和调试信息,是一件比拟麻烦的事件。 比方 Jerry 之前写过一篇文章:在 Windows 笔记本上调试运行在 iOS 设施上的前端利用,介绍了如何通过近程连贯的形式,间接在 Windows 电脑上,对运行在 iOS 设施 Safari 浏览器里的 Web 利用进行单步调试: 当然,如果仅仅须要在挪动设施比方手机上运行一下 Web 利用,而后想查看其打印的调试信息,用上述近程调试的形式就未免大材小用了。 vConsole 是腾讯公布的一个工具库,从其取得的超过一万三千个 stars,就晓得这个库的受欢迎水平: vConsole 的一种用法是,将其库文件下载到本地后,在网页的 script 标签里援用。创立一个 VConsole 实例后,依然采纳失常的 console.log 打印调试信息或者日志。 此时渲染出的网页右下角,会呈现一个绿色的 vConsole 按钮: 点击之后,能关上一个相似 Chrome 开发者工具的面板,从而在外面可能看到应用程序应用 console.log 打印出的调试信息。 ...

June 21, 2021 · 2 min · jiezi

关于ui:SAP-UI5-应用-indexhtml-里-datasapuiresourceroots-指令的含义和作用

如下图所示: <script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js" data-sap-ui-theme="sap_bluecrystal" data-sap-ui-libs="sap.m, sap.ui.comp" data-sap-ui-bindingSyntax="complex" data-sap-ui-compatVersion="edge" data-sap-ui-preload="async" data-sap-ui-resourceroots='{ "sap.ui.demo.CombineLatest": "./" }'></script>我刚学习 SAP UI5 时,对 data-sap-ui-resourceroots 的作用很是费解。 浏览咱们开发的整个 SAP UI5 我的项目资源,无论是 Component.js: 还是视图的控制器: 还是视图的 id 自身,都蕴含了 sap.ui.demo.CombineLatest 的前缀: 如果咱们把 index.html 里的 data-sap-ui-resourceroots 指令去掉: 会发现利用根本无法加载了,Chrome 开发者工具里报了很多资源文件无奈加载的谬误。 摘取其中一条谬误音讯进去剖析。当初 Component.js 的加载门路为: https://sapui5.hana.ondemand.... 显然,这个门路是继承自 index.html 里 id 为 sap-ui-bootstrap 里的 src 属性定义的 SAP UI5 库文件: 咱们工程文件里的 Component.js, 其 id 为 sap.ui.demo.CombineLatest.Component: SAP UI5 框架在加载时,将 id 转换成 url: ...

June 16, 2021 · 1 min · jiezi

关于ui:SAP-UI5-应用-XML-视图的加载逻辑分析

工作:剖析 SAP UI5 root XML 视图的加载逻辑。 鼠标放到 initiator 这一列上,找到调用栈的 UIComponent.js 的 createContent 办法: 能够看到,这里的逻辑是,从 manifest.json 里解析出 root view 定义,而后实例化该视图。 咱们再来看看另一个 root 视图加载失败的 SAP UI5 利用: Access to XMLHttpRequest at 'https://sapui5.hana.ondemand....' from origin 'http://localhost:3002' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.也胜利解析出 root view 的名称了: 应用 ajax 加载 xml view: 正确的 path: 谬误的 path: ...

June 16, 2021 · 1 min · jiezi

关于ui:使用-SAP-UI5-CLI-命令行工具构建和运行-SAP-UI5-应用

源代码 Github 地址:https://github.com/wangzixi-d... 本地门路:C:\Code\frontend-ui5-mssql 本文介绍 SAP UI5 Tools. package.json 里定义如下依赖:@ui5/cli npm install 装置依赖后,在 node_modules 文件夹下,发现了 @ui/cli 文件夹,其 readme.md 介绍提到,SAP UI5 CLI,是 SAP UI5 Tooling 的一部分。 这个 SAP UI5 工程里蕴含文件:package.json,定义了 rimraf 依赖。npm run-script build 命令,首先删除 dist 文件夹,而后执行 UI5 CLI 进行构建。 build 生成的 dist 文件夹: 最初 npm run-script start,即可启动这个 SAP UI5 利用: 更多Jerry的原创文章,尽在:"汪子熙":

June 16, 2021 · 1 min · jiezi

关于ui:一种简单地实现-SAP-UI5-Master-detail-页面的方法

实现成果如下图所示: app view 的实现代码: // @ts-nochecksap.ui.jsview("jerrylist.view.App", { getControllerName: function () { return "jerrylist.view.App"; }, createContent: function (oController) { // to avoid scroll bars on desktop the root view must be set to block display this.setDisplayBlock(true); this.app = new sap.m.SplitApp(); // load the master page var master = sap.ui.xmlview("Master", "jerrylist.view.Master"); master.getController().nav = this.getController(); this.app.addPage(master, true); // load the empty page var empty = sap.ui.xmlview("Empty", "jerrylist.view.Empty"); this.app.addPage(empty, false); return this.app; }});代码第 13 行创立的 sap.m.SplitApp, 实际上是 SplitContainer: ...

June 16, 2021 · 3 min · jiezi

关于ui:关于纹理勾选sRGB的疑惑

1)对于纹理勾选sRGB的纳闷2)开启光照导致面片数减少3)UGUI的Image批改材质属性疑难4)UniWebView界面如何显示到Unity界面之后5)Timeline的Internal_CreatePlayable开销很大 这是第254篇UWA技术常识分享的推送。明天咱们持续为大家精选了若干和开发、优化相干的问题,倡议浏览工夫10分钟,认真读完必有播种。 UWA 问答社区:answer.uwa4d.comUWA QQ群2:793972859(原群已满员) RenderingQ:在UWA上搜到一个问答:https://answer.uwa4d.com/question/5bd1724fae74300ab0497bed,论断是:linear space + gamma texture,勾sRGBlinear space + no gamma texture,不勾sRGB如同Gamma Space上面这个sRGB勾不勾没有影响? Player Setting外面的color space设置标识导入的图片是在Gamma Space中还是Linear Space中创立的,文档外面没有说分明,心愿大佬能够解释下。 依据这里的探讨:https://forum.unity.com/threads/confusion-about-gamma-vs-linear.496053,如果勾了sRGB之后Unity是做了gamma反改正,即tex2D进去的value应该是pow(origin_color_value, 2.2),这里假如gamma值是2.2,那么在frag中,R通道的值应该就是反改正过的,那么在heatmap的u的两头值应该是0.5左右,也就是下面那个探讨帖外面第一个headmap所示,但实际上却是第二个heatmap所示。 外面说是因为用color值做data而不是color的起因,这个解释太困惑了,因为不论做data还是做color,他们都是数字,数字的比拟是不会出错的。 A1:Gamma Space上面这个sRGB 勾不勾的确没有影响。在Linear Space下,如果勾了sRGB 之后,Unity是做了Remove Gamma Correction。那么图中值为0.5灰度,即u=0.5的那一列的灰度,通过Remove Gamma Correction,失去的值是0.25,如下图所示:X轴代表光照强度,Y轴代表灰度值 蓝色线的Y值代表人眼的灰度值,视觉灰度值;红色线代表物理空间的理论灰度值,与光照强度成正比。 Photo突变图中u=0.5的那一列的灰度值为0.5,是视觉灰度值,通过Remove Gamma Correction之后,失去理论灰度值0.25。在Fragment Shader中参加计算。所以显示的是第二张heat map。 感激June@UWA问答社区提供了答复 A2:能够参考这篇文章:https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-24-importance-being-linear 说下我的了解:个别状况下,咱们看到的图片是在“显示器空间”,在显示器中看到的图片色彩是失常的,是因为这个图片自身是通过GammaCorrection的,就是原始的数值通过1/2.2次幂操作,对于这样的图片,咱们能够了解为是非线性的。在Unity抉择了Linear空间后,对于这样的非线性的图片,须要将其勾选sRGB能力正确地渲染。图片勾选sRGB后,在内存中的格局会变成ETC2_EAC_RGBA8_SRGB的格局(假如图片抉择ETC2 8bits的压缩格局),如下图: 不勾选sRGB,是ETC2_RGB8_UNORM格局,如下图: 对于这种_SRGB后缀的格局,GPU在进行纹理采样的时候,会主动将其移除GamaCorrection,行将数值做2.2次幂操作,然而不会对原始数据做批改。在Gamma空间中,不管勾选还是不勾选sRGB,格局都是ETC2_RGB8_UNORM。 说回题主的问题:在Gamma空间中,渲染出的后果是均等的4种颜色,因为在Gamma空间中,是不会做移除GammaCorrection操作的,这阐明,这个“非线性图片”在u=0.5的时候(u是纹理uv坐标),纹理中的data value=0.5,当变成了Linear空间后,data没有发生变化,还是在u=0.5的时候data=0.5,勾选了sRGB后,在进行纹理采样的时候,GPU将值做了2.2次幂操作,就变成了0.25左右,因而当u<=0.5的时候,color.r<=0.25,依据Shader计算color.r<0.25时都是红色,即u<=0.5时,都是红色,于是就有一半色彩都是红色了。 感激Xuan@UWA问答社区提供了答复 RenderingQ:Unity显示的面数,应该是这一帧上传到GPU的所有顶点数据所决定的,那为什么GPU不会对这部分做个缓存呢?因为开启光照时,面数减少,相当于屡次上传了模型的顶点数据,这部分如果缓存下来,不是能够晋升很大性能吗?如果GPU不能做这个缓存,是不是说GPU的缓存太小,没有这个空间,或者是GPU并不能确认哪些要缓存?如果是改引擎代码,能够针对我的项目去做缓存。 A:光照自身着色不会影响DrawCall和面数。实时暗影须要在渲染一张暗影用深度图,相当于换了个视角进行渲染,数据有别于以后相机。能够开FrameDebugger看一下整个渲染过程。感激StriteR@UWA问答社区提供了答复 UGUIQ:UGUI的Image能通过设置MaterialPropertyBlock来批改材质属性吗?怎么设置? A:UGUI用不了材质块,能够参考:https://forum.unity.com/threads/big-problem-with-lacking-materialpropertyblock-for-ui-image.506941感激范世青@UWA问答社区提供了答复 UGUIQ:有一个安卓端显示网页的插件UniWebView,默认会显示到Unity界面的下层,挡住Unity所有的显示。请问怎么让安卓的原生界面成为背景,让Unity的UI成为前景,Unity的背景自身也要通明。 A:要让UniWebView跑到Unity界面前面去,官网文档上说是不行的。https://docs.uniwebview.com/guide/faq.html 对于Unity背景透明度放弃,在Player Settings外面有个勾选项应该能够达到目标。 感激Xuan@UWA问答社区提供了答复 PlayableQ:咱们的特效是应用Timeline进行制作的,最近进行性能测试的时候发现,Internal_CreatePlayable办法的开销很大,然而只有一个轨道,所以很蛊惑这个开销跟什么无关? 我在PC上加载了同样的一个对象,Internal_CreatePlayable须要18.55ms,而后我在PC上创立了Cube加了一个Timeline成果,Internal_CreatePlayable就须要4ms左右。 A:Timline首次CreatePlayable就是比拟耗时,我这边一个简略的Timeline资源首次耗时500+ms。 不过能够通过把CreatePlayable的步骤放在场景加载时进行来防止游戏过程中的卡顿。加载Timeline资源后,调用PlayableDirector.RebuildGraph就会进行CreatePlayable,相当于Prewarm。尔后,再调用Play(),就不会进行CreatePlayable。再调用RebuildGraph,因为有了缓存,也不会有高耗时。 下图为首次调用RebuildGraph的耗时。 下图为第二次调用RebuildGraph的耗时。 感激Prin@UWA问答社区提供了答复 封面图来源于:URP Learn一个用于学习Unity Universal Render Pipeline的我的项目。https://lab.uwa4d.com/lab/6010aca80f247485d9c39878 ...

June 16, 2021 · 1 min · jiezi

关于ui:SAP-UI5-使用-Smart-Control-的一个具体例子

咱们在本地 Visual Studio Code 里, 依照上面两篇文章,实现了 SAP UI5 的利用开发, 应用 yo 命令行向导创立 SAP UI5 利用应用 yo 命令行向导给 SAP UI5 利用增加一个新的视图最初能失去一个列表,成果如下: 本文咱们应用 SAP UI5 提供的 Smart 控件来持续丰盛这个利用。 从上面三个 namespace 引入 Smart 控件: xmlns:smartFilterBar="sap.ui.comp.smartfilterbar"xmlns:smartList="sap.ui.comp.smartlist"xmlns:smartTable="sap.ui.comp.smarttable"xml view 的实现源代码: <mvc:View controllerName="tutorial.products.controller.Products" displayBlock="true"xmlns="sap.m"xmlns:smartFilterBar="sap.ui.comp.smartfilterbar"xmlns:smartList="sap.ui.comp.smartlist"xmlns:smartTable="sap.ui.comp.smarttable"xmlns:mvc="sap.ui.core.mvc"><Page id="Products"> <smartFilterBar:SmartFilterBar id="smartFilterBar" persistencyKey="UniqueAndStablePersistencyKey" entitySet="Products" considerSelectionVariants="true" /> <smartList:SmartList id="smartProductList" smartFilter="smartFilterBar" entitySet="Products" expandFields="Category" header="Products List" showRowCount="true" showFullScreenButton="true" enableAutoBinding="true"> <smartList:listItemTemplate> <StandardListItem id="listTemplate" type="Navigation" press="handleListItemPress" title="{ProductName}" info="{= ${UnitPrice} + ' ' }" description="{Category/CategoryName}" /> </smartList:listItemTemplate> </smartList:SmartList></Page></mvc:View>npm run start 启动工程,最初看到的 Smart List: Smart Filter 控件提供的开箱即用的性能: 更多Jerry的原创文章,尽在:"汪子熙":

May 3, 2021 · 1 min · jiezi

关于ui:SAP-UI5-应用的中文乱码问题

如下图所示: 我应用 sap-language=ZH 的 url 参数,试图拜访我 SAP UI5 利用的中文版时,发现题目显示为乱码: https://er9.wdf.sap.corp:4430... 这是我的 i18n_zh.properties 文件: 解决办法:在 i18n_zh.properties 里间接输出中文对应的 unicode,而不是中文字符自身: 最初乱码隐没了: 更多Jerry的原创文章,尽在:"汪子熙":

May 3, 2021 · 1 min · jiezi

关于sap:SAP-Cloud-Application-Programming-CatalogService-默认的路径

我的 Service 定义,没有显式申明 service url: 最初 cds watch 的后果:该 Service 的名称:AdminService,去掉了后缀 Service,再把后面的 Admin 转成 lowercase 的后果:admin 同理: 我的 service: 更多Jerry的原创文章,尽在:"汪子熙":

April 11, 2021 · 1 min · jiezi

关于sap:SAP-Cloud-Application-Programming-bookshop-例子的-Fiori-Preview

进入 bookshop 文件夹,命令行 cds watch,以 development mode 启动利用: 点击超链接,即可通过 Fiori Elements 预览这些 service: 预览的 url 具备如下格局: http://localhost:4004/$fiori-... 查问 Authors 的 OData 申请 url: http://localhost:4004/admin/$... 返回的数据: 从运行时加载的 SAP UI5 这些源文件能看出,这个预览 (preview) UI 是一个典型的 Fiori Elements 利用: 更多Jerry的原创文章,尽在:"汪子熙":

April 11, 2021 · 1 min · jiezi

关于ui:干货放送界面控件DevExpress开发常用知识点全解

点击获取工具>> 应用DevExpress控件来做我的项目开发曾经有很长一段时间了,在摸索开发到客户刻薄要求的过程中,其中碰到过很多问题须要解决的,随着一个个问题的解决,也留下很多对DevExpress控件的应用教训及教训,综合设计到的多个我的项目的问题,对这些开发罕用的要点进行总结,不便他人也不便本人。提供这些解决办法,一个能够疾速利用到我的项目中,二个也能够作为对界面开发的更高要求看待本人的我的项目,使得本人的货色更加完满,更加受欢迎。 一、GridControl控件的数据显示的款式管制 如上两图所示,咱们有时候须要管制列表拜访过的色彩变动,或者是工夫显示格局等内容,这个时候设置GridView的RowCellStyle即可实现,如下所示。 `this.gridView1.RowCellStyle += new DevExpress.XtraGrid.Views.Grid.RowCellStyleEventHandler(gridView1_RowCellStyle); void gridView1_RowCellStyle(object sender, DevExpress.XtraGrid.Views.Grid.RowCellStyleEventArgs e){if (e.Column.FieldName == "PublishType"){if (e.CellValue != null && e.CellValue.ToString() == "中介"){e.Appearance.BackColor = Color.DeepSkyBlue;e.Appearance.BackColor2 = Color.LightCyan;}}if (e.Column.FieldName == "PublishTime"){e.Column.DisplayFormat.FormatString = "yyyy-MM-dd HH:mm:ss";} if (e.Column.FieldName == "Title"){string id = this.winGridViewPager1.gridView1.GetRowCellDisplayText(e.RowHandle, "Id");if (historyDict.ContainsKey(id)){e.Appearance.BackColor = Color.DeepSkyBlue;e.Appearance.BackColor2 = Color.LightCyan;}}}` 二、在LayoutControl布局中固定控件宽度 固定宽度后的实在成果。 为了使得界面统一性及更好的控制性,咱们个别应用LayoutControl布局控件作为咱们增加控件的布局容器,然而这个控件默认是对其中的控件进行按窗口比例进行缩放的,有些客户就不喜爱这些特点,因为他们的显示器可能是30寸的(夸大一点点,不过很多宽屏的),这样很多输入框就会被拉得很长,这样小小一个输入框,可能有很长的一段空白的间隔,那样可能真的不难看,如下图所示。 设置固定宽度,其实不是很麻烦,须要设置几个属性即可 设置控件的SizeConstraintsType为DevExpress.XtraLayout.SizeConstraintsType.Custom;设置控件的FillControlToClientArea 为False设置控件的ControlMaxSize的大小(必要时也能够设置ControlMinSize),设置例子如下所示。 三、GridControl中的GridView内容打印 因为GridView的良好封装性,实现打印的代码很简略。 `private void menu_Print_Click(object sender, EventArgs e){PrintableComponentLink link = new PrintableComponentLink(new PrintingSystem());link.Component = this.gridControl1;link.Landscape = true;link.PaperKind = System.Drawing.Printing.PaperKind.A3;link.CreateMarginalHeaderArea += new CreateAreaEventHandler(Link_CreateMarginalHeaderArea);link.CreateDocument();link.ShowPreview();} ...

February 22, 2021 · 1 min · jiezi

关于ui:SAP-UI的加载动画效果和幽灵设计Ghost-Design

这是Jerry 2021年的第 14 篇文章,也是汪子熙公众号总共第 285 篇原创文章。 在本篇文章之前,Jerry 印象最深的幽灵,应该要算《星际争霸I》里人族可能隐形的地面单位 Wraith( 幽灵战机 ),以及能施放核弹的 Ghost( 幽灵特工). 上周 Jerry 做 SAP Spartacus 开发时,接触到一个新的和幽灵相干的术语: Skeleton Design(Ghost Design) 读了帮忙文档后,发现该名词对我来说只不过是旧瓶装新酒罢了。 本文目录SAP UI5 Busy Dialog应用代理模式( Proxy Pattern ) 进步 SAP UI5 大尺寸图片的加载体验SAP Spartacus Spinner 控件SAP Spartacus 幽灵设计我对应用软件的 User Experience 即用户体验畛域知之甚少。在 SAP 外部,有专门的用户体验设计师负责这个畛域,因而我也不分明 Skeleton / Ghost Design 精确的中文翻译是啥,权且就直译成“幽灵设计”吧。 在我看来,无论是幽灵设计,还是之前 SAP UI5 提供的页面加载动画成果,都是改善用户应用体验的一种伎俩:提醒用户以后页面正在加载后盾数据,或是执行一些比拟费时的操作。 SAP UI5 Busy DialogJerry 从2014年开始应用 SAP UI5 进行 Fiori 开发,经验了 Fiori 1.0 到 2.0 的版本迭代。还记得解决的第一个 CRM Fiori 利用 My Opportunities 的 bug,症状就是批改了 Opportunity 数据之后,用户能够短时间内疾速点击下图的 Save 按钮,从而产生多个到 CRM 后盾的 OData 保留申请。 ...

February 21, 2021 · 2 min · jiezi

关于ui:介绍一个能开发简单SAP-UI5应用的在线IDEStackBlitz

这是Jerry 2021年的第 14 篇文章,也是汪子熙公众号总共第 285 篇原创文章。 在本篇文章之前,Jerry 印象最深的幽灵,应该要算《星际争霸I》里人族可能隐形的地面单位 Wraith( 幽灵战机 ),以及能施放核弹的 Ghost( 幽灵特工). 上周 Jerry 做 SAP Spartacus 开发时,接触到一个新的和幽灵相干的术语: Skeleton Design(Ghost Design) 读了帮忙文档后,发现该名词对我来说只不过是旧瓶装新酒罢了。 本文目录SAP UI5 Busy Dialog应用代理模式( Proxy Pattern ) 进步 SAP UI5 大尺寸图片的加载体验SAP Spartacus Spinner 控件SAP Spartacus 幽灵设计我对应用软件的 User Experience 即用户体验畛域知之甚少。在 SAP 外部,有专门的用户体验设计师负责这个畛域,因而我也不分明 Skeleton / Ghost Design 精确的中文翻译是啥,权且就直译成“幽灵设计”吧。 在我看来,无论是幽灵设计,还是之前 SAP UI5 提供的页面加载动画成果,都是改善用户应用体验的一种伎俩:提醒用户以后页面正在加载后盾数据,或是执行一些比拟费时的操作。 SAP UI5 Busy DialogJerry 从2014年开始应用 SAP UI5 进行 Fiori 开发,经验了 Fiori 1.0 到 2.0 的版本迭代。还记得解决的第一个 CRM Fiori 利用 My Opportunities 的 bug,症状就是批改了 Opportunity 数据之后,用户能够短时间内疾速点击下图的 Save 按钮,从而产生多个到 CRM 后盾的 OData 保留申请。 ...

February 21, 2021 · 2 min · jiezi

关于ui:全世界第二受欢迎的-React-UI-组件库一夜之间不见了

据 Ant Design 官网账号示意,Ant Design 在 Github 上的仓库在没有任何起因的状况下忽然隐没了,团队正在分割 GitHub 官网寻求帮忙和反馈。 截止发稿工夫, Ant Design 官网 GitHub 账号页面依然显示 404。 全世界第二受欢迎的 React UI 组件库Ant Design 是蚂蚁金服推出的一套企业级 UI 设计语言和 React 组件库,从 2015 年推出开始便受到宽泛的关注与应用,Ant Design 官网的题目也设置为: 全世界第二受欢迎的 React UI 组件库。 作为一个设计体系,Ant Design 蕴含的不仅仅是一个组件库。除了耳熟能详的 Ant Design React 外,还有 Angular 版本的 NG-ZORRO、Ant Desin Mobile、Ant Design Landing、HiTu React,以及来自社区志愿者的 Ant Design Vue。 垂直方向开箱即用的中台前端 / 设计解决方案 Ant Design Pro 和对应的区块市场,以及衍生组件库 Pro Layout 和 Pro Table。设计上也提供了十分多的标准文档以及相干的设计资产。 一夜之间隐没,起因不明 账号页面隐没的具体起因目前尚未颁布,有网友示意或者是在从 GitHub 向 Gitee 进行迁徙,也有网友示意疑似被黑客入侵导致。 ...

February 15, 2021 · 1 min · jiezi

关于ui:逼疯UE设计师不可不知的提升产品用户体验的10个测试方法

【摘要】用户体验的形容比拟主观,产品性能的可用性、可靠性、性能等都会影响用户的应用体验,比方性能bug问题也会说体验不好,程序解体也会说体验不好,性能卡顿会说体验不好,那是不是都在用户体验测试的范畴呢?测试人员如何无效地进行用户体验测试 1、什么是用户体验?UE != UIUI:User Interface 用户界面 用户界面其实是一个比拟宽泛的概念,指人和机器互动过程中的界面,以车为例子,方向盘、仪表盘、换档器等都属于用户界面。 当初个别把屏幕上显示的图形用户界面(GUI :Graphic User Interface)都简略称为UI。 UE: User Experience 用户体验 用户体验指用户在应用产品过程中的集体主观感触。即用户在应用一个产品之前、应用过程中、应用后的整体感触,包含行为、情感、爱好、生理和心里反馈、成就等各个方面。 艰深的讲用户体验是整体的应用感触,包含受品牌影响,用户集体应用教训的影响,总之就是让用户用着爽。有良好体验的事物易用且使人愉悦,或至多让人不加思考就能正确应用。 尽管良好的美学设计有助于进步用户体验,但用户体验实质上讲的不是杰出外观和感觉。为产品设计杰出的外观应该是最初一步,此前你要先把需要迭代实现,而后解决更形象的用户体验问题。 所以UE远不止网站外观这么简略,UI只是其中的一部分。 2、用户体验的重要性有钻研认为“每在用户体验的钻研上破费1美元,将取得100美元的收益”。 用户体验的重要性当初已是产业界的共识,是一种新的产品竞争力。 举个最典型的例子,乔布斯和他的苹果。 从某种意义上讲,苹果公司的胜利来自对人们如何应用电脑设备的透彻了解,以及开发“酷比了的产品”的高度承诺。 能够说,乔布斯的每一个产品翻新,都在颠覆人们的传统认知,并在其中把“用户体验“做到了极致,凭一己之力,彻底改变了手机市场的整体格局,在业界留下了一座不朽的丰碑。 用户十分看重体验的愉悦感和价值感,一旦不能满足,可能会迅速抉择其余可代替的产品,对于一个产品或者企业来说,失去客户是致命的。 3、如何进行无效的软件产品的用户体验测试用户体验的形容比拟主观,产品性能的可用性、可靠性、性能等都会影响用户的应用体验,比方性能bug问题也会说体验不好,程序解体也会说体验不好,性能卡顿会说体验不好,那是不是都在用户体验测试的范畴呢? 我认为不是,性能的可用性、可靠性、性能等都有成熟的测试方法,用户体验测试应该在产品的性能可用、牢靠、满足性能要求等根底之上的,更多关注的是用户感知和用户反馈,强调的是在产品可用性的根底上给予产品感触的反馈。 同样客户体验的晋升应该建设在产品可用性的根底上,否则一堆的bug体验怎么也不会好的;在可用性的根底上,再思考客户应用习惯、视图成果和交互成果等UCD方面的设计,能力达到极致体验。 那么用户体验测试的办法有哪些? 个别业界的用户体验测试包含考察问卷、用户访谈、用户测试(众测)、眼动仪、脑动仪等,其中较实用的办法为考察问卷、用户访谈、用户测试(众测),都是通过用户体验反馈来评估产品体验好坏的。 然而这种办法更多是由用户来实现,并不适宜服务测试。 那服务测试如何来测试用户体验呢? 咱们能够从用户体验设计来动手。 对于用户体验设计,业界有较多的设计办法,比方用户体验五因素、蜂巢模型、用户体验金字塔、5E、尼尔森十大可用性准则等。 用户体验五因素: 蜂巢模型: 用户体验金字塔: 5E: 尼尔森十大可用性准则: 状态可见准则环境贴切准则·撤销重做准则一致性准则防错准则易取准则灵便高效准则易扫准则容错准则人性化帮忙准则在咱们的实际中,最终抉择以尼尔森十大可用性准则为基准,再联合具体的业务,对每个个性、场景进行剖析,把每个准则进行实例化,生成具体可执行的测试用例,从测试的角度来验证产品的可用性。 对于尼尔森十大准则的具体解释,大家可在网上搜寻,资料还是很丰盛的,这里不再详述。 对于咱们设计的测试用例,去除产品强相干信息后,供大家参考,欢送大家批评指正: 本文分享自华为云社区《逼疯UE设计师,不可不知的晋升产品用户体验的10个测试方法》,原文作者:xyyzxyyz 。 点击关注,第一工夫理解华为云陈腐技术~

February 7, 2021 · 1 min · jiezi

关于ui:SAP-UI5和Angular的函数防抖Debounce和函数节流Throttle实现原理介绍

这是Jerry 2021年的第 11 篇文章,也是汪子熙公众号总共第 282 篇原创文章。 Jerry之前的文章 SAP UI5 OData流言粉碎机:极短时间内发送两个Odata request, 前一个会主动被cancel掉吗,介绍过SAP成都研究院CRM Fiori开发团队开发过的一个Live Search的场景。 用户创立Opportunity,保护Account字段,每输出一个字符,都会触发SAP UI5 Input控件的liveChange事件。在该事件的onAccountInputFieldChanged处理函数里,依据用户输出,发送OData申请到后盾进行查问。 如果用户输入速度很快,则在短时间内,会有多个OData申请发送到后盾,进而呈现Jerry文章里形容的OData申请被cancel的状况。 最近Jerry做SAP Spartacus开发,遇到了同样的场景。因而通过本文把本人最近所学总结一下,记录下SAP UI5和Angular里如何应用函数防抖(Debounce)和函数节流(Throttle)来防止短时间内触发高频次函数调用的状况呈现。 为了便于解说,Jerry做了一个只蕴含一个Input控件的SAP UI5页面。源代码地址. 在Input里输出字符,会触发liveChange事件,将以后Input的最新内容,发送到一个我本人开发的后盾服务去。该后盾服务什么也不做,只是简略将收到的内容返回给UI. 这个SAP UI5页面里的Input控件的liveChange事件处理如下: 从Chrome控制台打印的输入来看,我在一秒钟之内,间断疾速输出了1234共4个字符,一共产生了4个发送往后台的申请。 SAP UI5如何应用函数防抖(Debounce)来升高函数调用的频次函数防抖(Debounce),最早源于机械开关和继电器的术语“去弹跳”,行将多个信号合并为一个信号。 设想一个大家现实生活中都会遇到的场景:进电梯。电梯都有一个主动敞开门的超时工夫,假如为2秒。当电梯检测到有人进入时,会重置这个2秒的计时器。如果下一个2秒之内,没有新的乘客进入电梯,电梯门才会主动关上。 电梯提早关门这个场景,就是一个典型的函数防抖的事实例子。电梯关门的行为就是“函数”,通过电梯门的主动敞开超时工夫,2秒,来提早电梯门的敞开动作的执行,从而升高电梯门的敞开频率,这就是“防抖”。 能够设想,如果电梯门的主动敞开没有设定超时工夫,而是检测到没有人进出之后,立刻敞开,这样会大大增加电梯门开合的频率,既节约能源,也不平安。这就好比Jerry本文结尾提到的例子:既然我短时间内输出了字符1234,我冀望在UI看到的,是后盾服务接管到1234后返回的后果。至于后盾如何对前三个申请,即字符1,字符12和字符123进行解决,我不再关怀。 咱们能够仿照电梯门敞开超时工夫的设定,来给SAP UI5的函数调用实现防抖管制。 下图debounce变量是一个函数结构器,自身是一个函数,接管另一个函数fn作为输出参数,职责是通过闭包,将fn革新成一个具备防抖管制性能的新函数,该新函数通过第17行的return语句返回。 防抖工夫距离通过函数结构器另一个输出参数delay指定。 假如咱们指定的防抖工夫距离为3000毫秒即3秒,如果3秒之内,debounce函数结构器返回的新函数被一直调用,此时执行上图代码第19行,调用clearTimeout重置计数器,此时原始函数fn不会失去执行。这个场景能够类比成:在电梯关门超时工夫内,又有新的乘客进入,电梯超时计时器重置,电梯门不会敞开。 代码第20行,应用setTimeout重启超时工夫距离为3秒的计数器,3秒过后,如果JavaScript工作队列里没有其余待执行工作,则执行原始函数fn. 代码的第20行,好比电梯设施从新开启了3秒的超时定时器。 如果在期待的3秒之内,没有新的函数调用触发,则3秒过后,执行21行的原始函数fn;这好比电梯在3秒之内,始终没有新的乘客进入,则 3秒过后,电梯门主动敞开。 debounce函数结构器的应用形式也很简略。 代码第78行,将原始的sendRequest函数,以及3000毫秒的防抖工夫距离,传入debounce结构器,返回一个兼有数据发送性能和防抖性能的debounceVersion函数。在第85行原来调用sendRequest函数的地位,改为调用debounceVersion函数即可。 函数防抖性能的测试:我在同一分钟的第46秒,48秒,50秒,51秒四个工夫点,别离输出了1,2,3,4总共4个字符,然而在最初一次即51.996秒又过了3秒之后,才仅仅有一个申请发送到后盾:这阐明3秒的函数防抖距离失效了: SAP UI5如何应用函数节流(Throttle)来升高函数调用的频次上述函数防抖的实现存在一个问题,还是以电梯的例子来阐明。 构想有一个空间有限的电梯,关门的超时工夫为3秒。如果一直的有新的乘客以小于3秒的工夫距离进入电梯,则电梯门永远没有机会敞开——即函数永远得不到执行。 函数节流(Throttle)是另一种升高函数调用频次的思路,同函数防抖的区别是,后者能保障在指定的节流距离内,至多执行一次函数。 函数节流结构器的一个最简略的实现版本: 被节流器革新后的函数每次触发时,取一个以后零碎工夫戳,同前一次触发时取的工夫戳比拟。如果二者的时间差,大于等于结构器的输出参数delay即节流工夫距离,则进入第39行的else分支,触发原始函数fn;否则阐明节流工夫距离还未达到,应用第34行setTimeout,将原始函数fn,从新放入JavaScript事件队列内,提早执行: 函数节流版本的结构器应用形式,同函数防抖版本的结构器没有差异:将原始函数sendRequest传入结构器throttle,返回一个具备节流性能的新函数throttleVersion,在Input控件liveChange事件处理函数里,调用throttleVersion这个新函数即可。 函数节流的测试后果:我设置的节流工夫距离为3秒,从Chrome控制台打印输出能察看到,SAP UI5的确是大抵以3秒的工夫距离,向后盾发动的数据申请。 本文介绍的两种函数防抖和函数节流的实现代码,仅仅思考了最根本的状况,还有很多不欠缺的中央,有趣味的敌人能够在网络上搜寻,这方面的材料十分多,这里不再赘述。 Jerry之前的分享提到过,Angular是响应式编程开发库RxJS的重度使用者,后者提供了泛滥功能强大的Operators,使得Angular开发人员不必反复造轮子,就能轻易实现出具备函数防抖和函数节流的场景。 ...

January 29, 2021 · 1 min · jiezi

关于ui:制定一个计划开发总结-UI-设计和-Flutter-学习

我集体比拟偏好简洁的 UI 设计,惋惜不是相干业余,刚开始时齐全没有方向。像很多 UI 工程师一样(跟我司 UI 学的),我第一件做的事件就是上 dribbble 寻找灵感,看到适合的就珍藏下来,好日后进行模拟。 上面的截图这是过后珍藏的一些 UI 设计。 dribbble 上难看的设计切实太多,可是要把收集到的碎片重整为本人须要的样子就不太容易了。“灵感”收集得差不多了,接下来就本人摸索着在 Figma 画页面,同时也能把一些性能和交互确定下来。 UI 设计上花了很长时间,但看看最终上线后理论的 UI,与当初设计的也相差太大了????。边做边改,有的性能长期决定放到下个版本,有的界面实现之后没有达到预期的成果,有的性能决定不要了。随便一点吧,反正一切都是本人做主。 对于 FlutterFlutter 要应用 dart 语言进行开发,开始学习 Flutter 之前要简略看下 dart。能够跟着官网提供的 Tour 走一遍,看完也就差不多了。 而后学习 Flutter。Flutter 的学习文档很多,社区也很沉闷。中武官网的 开始应用 涵盖了所有初学者须要晓得的知识点。 学习 Flutter 比当初学习 Android 容易许多,两头没有碰到太多问题。而且用 Flutter 开发让人感觉比拟顺畅,你冀望达到什么成果,写好代码后热重载立马能看到成果,而且往往跟你的预期是统一的。比较烦人的是环境配置,之后的依赖下载(网络问题)等,耗时间。 学习 Flutter 时 Dart 的异步模型是必须要学习的,比拟重要,也有肯定难度。在进行一些耗时操作,如网络申请(这个利用将会应用到的 RPC Call),文件读取时就会用到。举荐两篇文章: Flutter(五)之彻底搞懂Dart异步Flutter/Dart中的异步还有一点比拟重要的是 BLoC(Business Logic Component) 模式,应用 BLoC 模式开发 Flutter App 能够无效的隔离 UI 显示 代码和 业务逻辑(状态治理) 代码。看看上面????这张经典的图例: BLoC 模式遵循上面的步骤解决用户事件:1) 用户与 App 交互产生事件(events),events 通过 Sinks 发送给 BLoC2) BLoC 对事件进行解决,并更新保护在 BLoC 内的状态数据3) BLoC 通过 Streams 将新的状态数据发送给 widgets,widgets 再对这些状态数据进行渲染。 ...

January 25, 2021 · 1 min · jiezi

关于ui:如何成功进行自动化的UI测试看完这篇文章你就懂了

点击获取工具>> 在抉择正确的工具来帮忙您胜利进行主动UI测试时,您须要理解以下内容。 为什么不能再疏忽主动UI测试? 只管面向代码的自动化测试工具曾经变得越来越广泛,但大多数开发公司都疏忽了自动化UI测试。这样做的次要起因是保护UI测试套件的老本,应用大多数/所有UI测试工具,实际上对应用程序UI的任何更改都会导致UI测试工具将整个应用程序标记为已损坏。后果,古代软件开发实际的大部分过程都是围绕UI与代码的准确拆散而组织的,因而能够在不接触UI的状况下测试代码。 现实情况是用户不与代码交互:用户与您的UI交互,从用户的角度来看,您的UI是您的应用程序,证实代码在成心疏忽UI的状况下无效的以后做法短少了重点。 与以后的实际相同,UI测试提出一个简略的主张:要证实您的应用程序已“筹备好投入生产”,您必须证实UI可能失常工作并驱动您的应用程序执行正确的操作。 一些基于工具的选项 随着DevOps和对用户验收测试的需要减少,这一要求变得越来越重要。 后果是UI测试工具失去了倒退,但这也使得开发者更难、也更容易获取正确的工具集。难点在于有更多抉择可供选择;容易在于有更多的工具对您有意义。 例如当查看UI测试时,能够在无代码工具和基于代码的工具之间进行抉择。 无代码工具容许测试人员通过与应用程序进行交互来创立UI测试,而该工具通过“察看”用户的交互和应用程序的响应来生成测试脚本。 这些工具利用“ UI即应用程序”范式,并且不须要测试人员比应用程序(及其相干的业务需要)理解更多。 另一方面,基于代码的工具要求测试人员编写脚本来通过代码(即在页面上查找按钮,而后从UI元素提取数据)来操纵UI。 然而,这些工具能够查看“副作用”,这些副作用不肯定显示在任何用户界面(或“能够作为测试的一部分进行拜访的任何用户界面”)中,并且能够解决各种响应,基于代码的工具的确要求测试人员晓得如何编写代码。 无代码工具使开发人员脱离了测试的要害门路,并受权用户创立对其无效的测试。 基于代码的工具反对更深刻、更彻底的探测、并解决各种响应,从而缩小谬误的数量(实际上,在应用程序失常运行时的故障报告)。 重要事项 无论您最终应用什么工具,都须要将它们集成到您的流程中,而不会障碍您交付应用程序……并在满足组织、用户和您本人的指标的同时做到这一点。 首先:您是否须要自动化的UI测试? 值得记住的是,测试的指标是将失败的老本从生产环境转移到开发环境中。 如果您的团队对以后的生产失败程度感到称心,并且不违心批改开发实际,那么您可能不须要自动化的UI测试。 自动化的UI测试如何合乎团队的战略目标? 第一个问题与第二个问题重叠:自动化测试如何适应团队文化?团队是否器重尽快向心愿应答高变化率的用户社区提供新性能,即便存在一些小故障?还是团队更须要高度牢靠的应用程序,这些应用程序会随着工夫的推移而稳固,因而能够满足严格的(兴许甚至是法规)规范? 反过来,这个问题与第三个问题重叠:主动UI测试将如何适应您的流程? 答案始于用户何时何地进行验收测试。例如如果有很长的工夫用户没有参加开发过程,那么利用用户的UI测试策略可能就没有意义。如果在团队中如果“编码器驱动的UI测试”是一个矛盾的话题(即只有最终用户会说出UI是否“正确”),那么基于编码器的办法就无奈适应您的工作形式。 最初一个问题:您能够利用哪些技能集和现有工具集? 例如,无代码测试仅在您领有一群不仅仅“应用”应用程序但有能力晓得在测试中什么是“正确”或“不正确”响应的用户时才有意义。 在开发人员方面,您心愿查看用于交付应用程序的工具链 - 利用团队在该工具链上的教训并与之集成能够为您带来真正的益处。 不过,乏味的是,在抉择UI测试工具时,用于构建应用程序的开发工具并不是特地重要,特地是对于Web应用程序而言。 **[Telerik Test Studio] 比起繁多的“ UI测试工具”,更须要一种为满足特定需要测试而配置的套件,最终会组合一个最佳的套件来满足您的特定需要,然而从繁多起源取得残缺的解决方案显然会更不便。 自动化UI测试畛域的供应商既器重灵活性,又器重与其余工具集成的反对。 例如,[Telerik Test Studio]反对无代码测试,反对将那些无代码测试转换为编码测试,将编码步骤与无代码测试联合在一起,并与第三方库集成以满足非凡需要。 意味着非程序员(例如QA团队或最终用户)能够创立测试,以证实零碎已实现用户心愿零碎执行的操作。 将这些无代码测试与编码测试无缝联合的能力意味着,当非程序员遇到阻碍时,开发人员能够扩大这些测试以解决“难以自动化”的场景。 创立无代码测试的能力然而请不要遗记这一点:依然不是对于工具的问题,而是这些工具是否反对您的指标、流程以及现有技能/工具链。如果您对这些内容有很好的理解,那么就能够获取在主动UI测试中取得成功的工具。

December 15, 2020 · 1 min · jiezi

关于ui:SAP-UI5和Angularjs事件处理机制的实现比较

Jerry最开始是用SAP UI5进行SAP CRM Fiori利用的开发。最近一段时间做SAP Spartacus开发,在用Angular,因而借这个机会将两个前端框架的事件处理实现细节做一个比拟。 SAP UI5事件处理通过button控件的attachPress办法注册一个Press事件点击的处理函数: button控件自身的实现是没有attachPress这个办法的,这一点能够从hasOwnProperty返回false来确认: Instead, it is provided by the node in the button instance’s prototype chain, EventProvider. 顺着SAP UI5 button原型链沿着继承关系向上寻找,最初发现在EventProvider里提供了attachEvent办法。如果对SAP UI5 button的原型链不相熟,能够参考我这篇文章:深刻学习SAP UI5框架代码系列之一:UI5 Module的懒加载机制 当咱们利用代码里调用attachPress时,传入这个函数的事件响应函数被退出到一个SAP UI5对立保护的事件处理注册表mEventRegistry里,这是一个键值对数据结构,key为应用程序注册的事件名称,值为咱们传入的 事件响应函数。如下图所示,key为press,值为fFunction: 当咱们点击了UI上的按钮之后,SAP UI5控件的Button.onclick办法会被调用,外面会fire一个Press事件。这里实现了浏览器原生的click事件到语义事件Press的转换。 SAP UI5依据press,到事件注册表mEventRegistry里去查找,将所有注册到该事件上的所有响应函数取出,放到一个数组aEventListeners里,遍历这个数组,逐个调用响应函数。 Angularjs事件处理下图是一个Angularjs利用,基于Angularks 1.2.18开发而成. 在界面上显示了硬编码之后的三个国家的人口,我心愿通过人口对这三个国家进行排序。 我给Country这一列通过ng-click指令注册了一个排序逻辑:sortField = 'name' Angular和SAP UI5一样,有本人的bootstrap阶段。在此阶段Angular框架做的事件之一,就是Angular框架会通过下图第964行代码即compile函数,遍历html DOM树。 如果发现有一个element attribute具备ng前缀,执行applyDirectivesToNode函数,为该节点增加一些非凡的逻辑。 Angular通过下列的三个步骤,对ng-click = "sortField = 'name'"进行响应函数注册: 第一步:解析蕴含ng-click= "sortField = 'name'"的HTML元素,创立一个wrapper fn. ...

December 5, 2020 · 1 min · jiezi

关于ui:SAP-UI5和Vue的数据双向绑定实现原理比较

Two-way data binding in UI5Two way data binding test: Control property change leads to model field changeTwo way data binding test: model field change leads to control property changeTwo way data binding in VueTwo way data binding test: model field change leads to control property changeTwo way data binding test: Control property change leads to model field changeStep1 – Model directive detectionStep2 – Generate source code of event handlerStep3 – Register event handler into physical DOM elementStep4 – Model field will be changed in onInput event handlerRecently when I do self study on Vue I find many articles in the internet with full of praise on Vue‘s reactive Two-Way Data binding trait. This fact makes me recall my self-study on UI5 early in year 2013 and at that time, the Two-Way Data binding was already supported by UI5. ...

August 29, 2020 · 5 min · jiezi

关于ui:SAP-WebClient-UI的白屏问题分析

IssueOnce product hyperlink in sales order line item is clicked, it is expected that product overview page is opened. Instead the empty screen is displayed now: Most efficient way to find root causeFollow this blog how to persist the UI exception so you can view them later to register the custom error log in your system. (1) Specify the error date and user name who has encountered with this error: ...

August 26, 2020 · 1 min · jiezi

关于ui:SAP-UI5应用白屏的原因分析

Today I am working on another incident and I get to know another kind of reason which will lead to empty screen issue in UI.My previous experience for potential reason of empty screen issue is, there must be some JavaScript execution error which causes the UI ceases to render. The issue I am working on today: Say in system A when I try to create a follow up Opportunity based on a lead, ...

August 21, 2020 · 3 min · jiezi

关于ui:使用调试的方式搞清楚SAP-UI5应用标题的更改方式

In Fiori launchpad, the page title will display default value “Home”. When I click a given tile to enter an UI5 application, the page title is changed for example from “Home” to “My Tasks”. Today I am dealing with an incident that once I have entered one application, the page title is not displayed correctly, see below error screenshot: In order to fix this issue, I need to know exactly which line has changed the value of page title.Since I don’t have any clue why page title becomes like this when I enter the erroneous application, so my thought is if I can test with an application where the page title works correctly, and if I can find out the line of code for title value assignment, then I can set breakpoint on that line and debug back with erroneous application. ...

August 21, 2020 · 2 min · jiezi

关于ui:对于一款软件而言完备的功能固然重要但交互体验也不该被忽视

个别状况下,软件开发的后期设计以及开发的过程中,满足其功能性的优先级要高于交互界面的好看性。以至于业界中泛滥软件平台的UI设计,与其齐备的性能造成落差。对于对美感稍有谋求和观赏能力的用户来说,应用时所感触到的微小割裂感可能会令其产生激烈不适。 这不难理解,毕竟呈现这种状况,都是开发者出于老本管制的思考,而对前端页面刻意弱化。但这样一来,软件产品的整体品质就呈现短板,用户会很容易觉察到其中的缺点与歹意。SO,一款优良的软件除了有齐备的性能之外,也要有足够好看敌对的用户交互界面。 这里举一个侧面的例子,看看LR.net疾速开发平台的UI与性能,能够做为一种参照或者规范。首先,其外部筹备了多套不同格调的Homepage界面,以满足不同人群的审美。 经典极简 冷酷商务 清爽慷慨 青春活力 自在恬适再者就是功能性方面,总之,一个软件开发平台该有的货色,它都能给到用户。 麻利开发麻利开发向导:表单、流程、数据等罕用性能配置向导 代码生成器:八套开发模板,生成类、页面、映射、表单、小程序等 通用图标:PC和挪动端图标 数据看板:BI大数据看板 表格组件:各类罕用表格 甘特图:理解我的项目进度 信息可视化:货架、生产线等 门户配置:企业门户 插件配置:框架内置及第三方插件 二维码生成:企业二维码生成 D3配置:动态数据展现 2.系统管理 行政区域:全国行政区划 数据字典:各我的项目个性查问 单据编码:合同、表单等文件编码 零碎性能:零碎性能分类展现 系统日志:日志类 LOGO设置:框架logo设置 数据权限:权限类 桌面配置:首页桌面性能配置 音讯治理:音讯类 多语言治理:内置中、英、繁,可拓展 微信企业号:企业号开发 任务调度:工作的执行 Excel配置:表格导入导出 数据管理:数据表、数据源、数据库连贯及常用字段 文件治理:文件类 3.单位组织 公司治理:总/分公司治理 部门治理:部门 岗位治理:岗位 角色治理:角色 用户治理:用户 4.表单利用 表单设计:设计罕用表单 表单治理:表单根底、条件、列表设置 表单实例:示例 5.流程利用 流程设计:人事、我的项目、购销等各类流程设计 流程工作:待办/已办流程 流程委托:委托别人解决流程 流程监控:已实现/未实现流程整体监控 签章治理:签章类(反对手写) 流程实例:示例(销假流程) 6.挪动治理 挪动开发向导:挪动表单、流程、数据等罕用性能配置向导 挪动性能:挪动端罕用性能 首页图片:挪动首页图 Logo设置:挪动logo 桌面设置:挪动端桌面配置 7.报表利用 报表公布:绑定已设计报表后公布 报表设计:图标、列表设计 业余报表:葡萄城业余报表 报表实例:罕用报表示例 简洁报表:洽购、收支、仓存、收支类报表 8.利用实例 OA办公:新闻、布告、日程、签章、导出模板、邮件核心 ...

August 17, 2020 · 1 min · jiezi

关于ui:思否开源项目推介丨UUI功能优先的通用-UI-组件库

开源项目名称:UUI 开源我的项目负责人:@孙扣扣 开源我的项目简介:性能优先的通用 UI 组件库 开源我的项目类型:团队开源我的项目我的项目创立工夫:2020 年 GitHub 数据:44 Star,2 Fork GitHub 地址:https://github.com/HackPlan/UUI负责人自荐做这个我的项目是因为咱们发现市面上的 UI 组件库大多数都是款式和性能一体的,也就是说无奈轻松的实现款式的自定义,后果是很多应用同一个 UI 组件库的我的项目,看起来就像是同一个格调。 UUI 心愿提供一个性能优先,款式可高度自定义的根底组件库。通过应用 UUI,各个我的项目能够依据本人的款式格调,轻松的定制本人的组件库。 拓展性方面,不同于其余组件库高度封装的设计,UUI 提供了极高的自定义空间,当性能不够用的时候,能够自行拓展。 UUI 最早是 2019 年底就开始外部开发,今年年初开始投入生产我的项目的应用。当初每周都在沉闷地更新。 我的项目个性一组开箱即用的有用的组件。基于 TypeScript 的类型安全性。弱小的组件款式自定义性能。 我的项目亮点组件品种丰盛:提供了较全面的根底组件、笼罩各类场景,组件个性丰盛、满足各种性能需要。反对高度拓展:为了更好定制自定义组件的性能,能够应用自定义组件扩大机制。风格化组件款式:反对灵便的款式定制,繁难生成多种格调,满足个性化产品需要。 思否举荐UUI 是 HackPlan 团队打造了一款性能优先的通用 UI 组件库,反对常见的 Breadcrumb、Button 等29 种组件,能够满足一些中小型业务的需要。 UUI 的文档展现形式让人眼前一亮,不再是单纯的演示 + 代码,而是融入了Actions、Story、Knobs、Performance、Accessibility 等形式来让使用者更具体的理解与学习应用 UUI。 该我的项目已入选「SFOSSP - 思否开源我的项目反对打算」,咱们心愿借助社区的资源对开源我的项目进行相干的宣传推广,并作为一个长期我的项目助力开源事业的倒退,与宽广开发者共建开源新生态。 有动向的开源我的项目负责人或团队成员,可通过邮箱提供相应的信息(开源我的项目地址、我的项目介绍、团队介绍、联系方式等),以便晋升交换的效率。 分割邮箱:pr@segmentfault.com

August 13, 2020 · 1 min · jiezi

第三章-路径和画笔工具的应用云图智联

第三章 路径和画笔工具的应用本章任务掌握AI中绘制路径的工具使用能够对所绘制的路径进行修改美化操作掌握AI中路径查找器的使用掌握AI中画笔工具的使用绘制路径学习目的: 学习在AI中使用工具去绘制一些路径绘制线条: 特点一:结合“描边面板设置该线段的端点状态; 特点二:直接点击工作区可以精确绘制具体的线条 特点三:绘制线条过程中,按住空格,可以跟随鼠标移动线条的位置 1."直线段工具" 直线段绘制小技巧: 1) 直接任意绘制线段; 2) 按住Shift可以水平,垂直或者45°角绘制直线段; 3) 绘制过程中按住“~“可绘制很多线; 4) 精确绘制线段———使用“直线段工具”单击工作区,在弹窗中设置精确数值; 2."弧线工具" 弧线绘制小技巧: 1) 拖动鼠标绘制的同时,按住Shift键,可得到X轴和Y轴长度相等的弧度; 2) 绘制时,按住键盘的C键可以控制弧度是开放或者闭合; 3) 绘制时,按住X键或者F键可以使弧线在“凹”“凸”曲线间切换; 4) 拖拽鼠标绘制同时,按住键盘上下键可增加减少弧线曲率半径; 5) 精确绘制弧线———使用“弧线工具”单击工作区,在弹窗中设置精确数值; 3."螺旋线工具" 螺旋线绘制小技巧: 1) 绘制时,住Ctrl键可保持涡形的衰减比例; 2) 绘制时,按住键盘上下键可增加减少涡形路径片段的数量; 3) 精确绘制螺旋线———使用“螺旋线工具”单击工作区,在弹窗中设置精确数值; 4."矩形网格工具" 绘制矩形网格小技巧: 1) 绘制时,方向键控制网格行数和列数;2) 绘制时,按住Shift键,可以定义绘制的矩形网格为正方形网格 3) 绘制时,按住C键,竖向的网格间距逐渐向右变窄。 4) 绘制时,按住V键,竖向的网格间距逐渐向上变窄。 5) 绘制时,按住X键,竖向的网格间距逐渐向左变窄。 6) 绘制时,按住F键,竖向的网格间距逐渐向下变窄。 7) 精确绘制矩形网格———使用“矩形网格工具”单击工作区,在弹窗中设置精确数值; 5."极坐标工具" 绘制极坐标小技巧: 1) 绘制时,按住Shift键或者Alt+Shift键可以绘制正圆形网格; 2) 绘制时,按住上下方向键,可以调整经线数量; 3) 绘制时,按住左右方向键,可以调整纬线数量; 4) 精确绘制极坐标———使用“极坐标工具”单击工作区,在弹窗中设置精确数值; 演示案例案例1:用矩形网格工具制作笔记本上表格 案例思路: 1)使用矩形工具绘制笔记本背景;2) 使用矩形网格工具结合该工具特点绘制网格数量; ...

June 30, 2020 · 1 min · jiezi

第二章-基本图形的绘制与编辑云图智联

第二章 基本图形的绘制与编辑本章任务掌握AI绘制基本图形的方法掌握对AI基本图形的编辑方法掌握AI基本图形的变换操作矢量图形的构成 学习目的:了解矢量图形的组成部分矢量路径 矢量路径可以理解为限制图形的边界,每个矢量图都有一条路径包围着 颜色 由“填充”和“描边”组成,“填充”和“描边”使矢量图形变得五颜六色。 绘制基本图形学习目的:学习在AI中使用5种工具去绘制一些基本图形在AI中绘制基本图形时,描边和填充是如何设置的。绘制基本图形的工具使用: 特点:每个工具单击空白工作区时可以精确绘制图形 1.“矩形工具”(M) 1)可以直接绘制任意矩形 2)绘制正方形 按Alt键绘制,是从指定的区域绘制按Alt+Shift键,是从中心开始绘制 2.“圆角矩形工具” 1)可以直接绘制任意圆角矩形 2)绘制正圆角矩形: 按Alt键绘制,是从指定的区域绘制 按Alt+Shift键,是从中心开始绘制 改变圆角大小——在绘制圆角矩形时,拖动鼠标情况下: 左方向键控制最小圆角右方向键控制最大圆角 上方向键增大圆角 下方向键减小圆角 3.“椭圆工具”(L) 1)可以直接绘制任意椭圆 2)绘制正圆: 按Alt键绘制,是从指定的区域绘制按Alt+Shift键,是从中心开始绘制 4.“多边形工具” 1)在绘制多边形时,拖动鼠标情况下: 上方向键增加边数下方向键减少边数 5.“星形工具” 1)在绘制星形时,拖动鼠标情况下: 上方向键增加角的数量下方向键减少角的数量 按Ctrl键向外拖拽,可以增加角的尖锐度 按Ctrl键向里收缩,可以减小角的尖锐度 按Alt键可以恢复默认角的尖锐度 描边和填充的使用:描边和填充” 1)在工具箱中下面的前景色和背景色是由填充和描边组成的,谁在前,改变的就是谁的颜色2)X键 描边和填充前后位置进行互换 3)Shift+X键 描边和填充颜色进行互换 4)在AI中描边的值可以输入小数 5) 可以设置单色填充和渐变填充 描边面板的使用: 1)描边的粗细2)端点——针对开放描边,可以设置平头、圆头、方头 3)边角——针对闭合描边,可以设置直角、圆角、切角 4)对齐描边——针对闭合描边,可以设置描边的居中、内侧、外侧对齐 演示案例案例1:使用“矩形工具、圆角矩形工具”制作UI图标 案例思路: 1)使用圆角矩形工具和矩形工具绘制,绘制时注意圆角的控制;;2) 使用“填充”效果进行填色; 3) 使用“直接选择工具”更改图形锚点,使图形变形; 4) 使用“选择工具”选中图形更改元素位置 案例2:用椭圆工具绘制天气图标 案例思路: 1)使用“圆角矩形工具”和“椭圆工具”进行绘制;2) 使用“填充”效果进行填色; 3) 使用“选择工具”选中图形更改元素位置 案例3:用星形工具制作游戏标志 案例思路: 1)使用“星形工具”、“椭圆工具”、“矩形工具”等进行绘制;2) 结合“渐变工具”和“渐变命令”进行渐变色填充, ...

June 30, 2020 · 1 min · jiezi

AI第一章笔记云图智联

Illustrator图形设计与形象设计本章任务了解AI软件与PS软件的区别了解AI软件的应用领域掌握AI软件的基本操作AI软件的介绍学习目的:了解AI软件的特点了解AI与PS软件的区别简单了解AI软件: 1) 它是全球最著名的设计软件之一 2) 它绘制出来的矢量图形可以任意放大、缩小且不失真 3) 它可以实现矢量图与位图的相互转化 位图与矢量图的区别:位图(点阵图)——由像素点组成,有像素限制 位图的产生 通过数码相机拍摄而来的通过设计或者其他软件绘制渲染而成 位图的优缺点 优点:色彩丰富,光影关系表现到位缺点:像素会失真,出现像素块(马赛克) 矢量图——是由计算机根据几何特性绘制的图形 矢量图的产生 是由一个点或者一条线组成,只能靠软件来绘制矢量图的优缺点 优点:放大不会失真缺点:色彩单一,光影关系表现不到位 AI软件与PS软件的区别: 绘图领域 AI是矢量图形设计; PS是位图(像素)图像设计; 放大后效果 AI放大后不失真 PS放大后会失真 画布及工作区 AI在新建画布时可同时建立多个画布; AI可在工作区中绘图也可在草稿区绘图; PS在新建画布时只能建一个画布; PS只能在工作区中绘图; AI设计的应用领域学习目的:了解AI设计的主要应用领域应用领域广告设计 排版设计 插画设计 包装设计 书籍装帧 VI设计 AI软件工作环境学习目的:对工作界面基本认识工作界面: 菜单栏工具箱草稿区绘图区调班首选项和性能优化 快捷键:Ctrl+K ;优化电脑性能实现思路 执行“编辑”-“首选项”进行自定义环境设置 Illustrator的文件管理新建文件: 新建文件方法: "文件"菜单——新建快捷键:Ctrl+N 新建文件注意事项: 名称: 根据设计内容命名 单位: 像素: 设计的内容在电子设备上显示 毫米 mm(国内单位,常用): 设计的内容需要打印印刷时 厘米 cm(国内单位): 设计的内容需要打印印刷时 出血: 出血值:3mm设计内容在裁切时,内容区域与裁剪边缘的误差 ...

June 16, 2020 · 1 min · jiezi

WijmoJS-V20190-Update2发布再度增强-React-和-Vue-框架的组件功能

前端开发工具包 WijmoJS 在2019年的第二个主要版本 V2019.0 Update2 已经发布,本次发布涵盖了React 和 Vue 框架下 WijmoJS 前端组件的功能增强,并加入更为易用且灵活的撤消/重做功能和模板字符串支持。 WijmoJS 前端开发工具包由多款灵活高效、零依赖、轻量级的纯前端控件组成,如表格控件 FlexGrid、图表控件 FlexChart、数据分析 OLAP 等,完美支持原生 JavaScript,以及 Angular、React、Vue、TypeScript、Knockout 和 Ionic 等框架,可用于企业快速构建桌面、移动 Web 应用程序。 在列举前端开发工具包 WijmoJS V2019.0 Update2 的全部功能之前,请下载最新安装程序,以便同步体验!>>前端开发工具包 WijmoJS 最新下载地址 前端开发工具包 WijmoJS V2019.0 Update2 的主要新特性有: React 框架下组件功能增强Vue 框架下组件功能增强仪表盘的自定义能力增强新增模板字符串常量Undo / Redo(撤销/重做)全新的分页控件FlexGrid 中的多区域选择FlexGrid 中性能调优 API全球化中更多语言支持React 框架下 WijmoJS 组件功能增强React 框架已经大受欢迎,WijmoJS 也一直在增强其在 React 框架下的各组件的功能。在新版本中,WijmoJS 提供了一些不错的方法来定义自定义模板,如在脚本中绑定具有 Items 标记的控件。 使用代码,即可在WijmoJS 中添加 MenuItem 和 MenuSeparator 组件。 而之所以这么做,是为了解决纯javascript菜单控件不能通过JSX标记定义所带来的不便之处。WijmoJS在与 React 框架深度结合后,在 JSX 标记中使用 React 组件及其属性绑定将会更加方便,实现以声明方式定义项目内容。 ...

September 19, 2019 · 1 min · jiezi

UI2CODE系列文章如何批量制造高质量样本

在 UI2CODE 项目中,我们大量使用了深度学习方法来做一些物体检测。而深度学习模型的训练,避免不了需要大量的样本,因此如何制造大量样本,来满足模型训练需要是我们必须要解决的一个问题。在这篇文章中,我们将介绍我们如何利用工具,批量泛化出大量样本,为模型训练提供数据保障。 1.样本现状我们的模型要解决的问题是在一个设计稿图片上识别出基础控件等信息,包括位置和类别。而它所需要的样本,主要存在两个问题: 数据量少:一个APP的页面是有限的,特别是针对单个APP做优化适配的时候,页面的数量是相对较少的,可能在几十到上百个。而模型的对样本数量的需求是巨大的,特别像较为复杂的模型,对数据量的要求至少是万级别的,单靠真实样本,是远远达不到要求的。标注成本高:物体检测的样本标注,不仅需要标注物体的类别,更需要标注出物体的具体位置,而一个样本上会存在多个物体标注。因此,这类样本打标成本非常大。2.样本获取途径获取样本,主要有几种途径。 对于真实样本,这类质量是最高的,要想训练出效果很好的模型,这类样本基本是必不可少的,但是由于这类样本数量少,成本高,因此还需要其他方法来补充样本量。 对于数据增广,这种方法简单快速,但是效果也有限,特别是对于我们 UI2CODE 里识别控件这个任务来说,做旋转等操作基本是无效的。 因此,我们需要利用样本Mock,来扩充我们的数据量,尽量模拟出质量又多,量又大的样本。这里我们选择的是利用Weex页面来进行样本的Mock泛化。(当然还有一些其它方法,比如利用 Android 的特性,在运行时的APP页面,抓取页面数据,经过过滤和清洗,得到带标注的样本,这里不做展开) 3.WEEX页面样本泛化在这里,我们介绍如何利用 Weex 页面,来批量泛化样本,并且得到样本标注的方法。 前端页面特点之所以选择使用前端页面来生成样本,是因为前端页面更多的是做一些数据展示,并且其拥有完整的 DOM 树,只要我们拿着DOM树就可以解析出里面的各个元素。 对于节点内容,只要我们改变元素内容即可。这样我们就可以由一个前端页面很方便地泛化出不同文字、不同图片的多个样本。 当然,我们的闲鱼APP上有大量的Weex活动页,这也是我们选择做Weex页面泛化的原因之一。 泛化思路我们需要的基础控件的分类有“文本”、“图片”、“Shape”这三类,对于一个页面来说,我们的文本和图片内容基本都是可替换的,因此我们解析出所有节点以后,对里面的文本和图片进行替换,再进行渲染就可以得到新的样本。 利用 Puppeteer 实现泛化要想得到Weex页面,需要有一个渲染容器,并且我们可以很方便地修改其内容。这里,我们选择了Google的Puppeteer,它是Google推出的可以运行 Chrome Headless 环境以及对其进行操控的js接口套装。通过它,我们可以模拟一个Chrome运行环境,并且进行操控。官方简介在这里. 首先启动一个不带界面的浏览器: const browser = await puppeteer.launch({ headless: true});启动一个页面,然后打开一个网站: const page = await browser.newPage();await page.goto(nowUrls, {waitUntil: ['load','domcontentloaded','networkidle0']});模拟IPhone6环境: await page.emulate({ 'name': 'iPhone 6', 'userAgent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', 'viewport': { 'width': 750, 'height': 1334, 'deviceScaleFactor': 1, 'isMobile': true, 'hasTouch': true, 'isLandscape': false }});搜索所需控件: ...

August 8, 2019 · 2 min · jiezi

Flutter网格型布局-GridView篇

1. 前言Flutter作为时下最流行的技术之一,凭借其出色的性能以及抹平多端的差异优势,早已引起大批技术爱好者的关注,甚至一些闲鱼,美团,腾讯等大公司均已投入生产使用。虽然目前其生态还没有完全成熟,但身靠背后的Google加持,其发展速度已经足够惊人,可以预见将来对Flutter开发人员的需求也会随之增长。 无论是为了现在的技术尝鲜还是将来的潮流趋势,都9102年了,作为一个前端开发者,似乎没有理由不去尝试它。正是带着这样的心理,笔者也开始学习Flutter,同时建了一个用于练习的仓库,后续所有代码都会托管在上面,欢迎star,一起学习。 经过上一篇对ListView组件的学习,我们已经对滚动型组件的使用有了初步认识,这对今天要学习的GridView组件十分有帮助。因为两者都继承自BoxScrollView,所以两者的属性有80%以上是相同的,用法非常相似。 而且如下图所示可见,GridView网格布局在app中的使用频率其实非常高,所以接下来就让我们来看看在Flutter中如何使用吧~ 2. 初识GridView今天我们的主角GridView一共有5个构造函数:GridView,GridView.builder,GridView.count,GridView.extent和GridView.custom。但是不用慌,因为可以说其实掌握其默认构造函数就都会了~ 来看下GridView构造函数(已省略不常用属性): GridView({ Key key, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController controller, ScrollPhysics physics, bool shrinkWrap = false, EdgeInsetsGeometry padding, @required this.gridDelegate, double cacheExtent, List<Widget> children = const <Widget>[],})虽然又是一大堆属性,但是大部分都很熟悉,老朋友嘛~除了一个必填参数gridDelegate外,全和ListView默认构造函数的参数一样,这也是文章开头为什么说掌握了ListView再学GridView非常容易的原因。 那么接下来,就让我们来重点关注下gridDelegate这个参数,它其实是GridView组件如何控制排列子元素的一个委托。跟踪源码我们可以在scroll_view.dart中看到,gridDelegate的类型是SliverGridDelegate,进一步跟踪进sliver_grid.dart可以看到SliverGridDelegate其实是一个抽象类,而且一共有两个实现类: SliverGridDelegateWithFixedCrossAxisCount:用于固定列数的场景;SliverGridDelegateWithMaxCrossAxisExtent:用于子元素有最大宽度限制的场景;2.1 SliverGridDelegateWithFixedCrossAxisCount我们先来看下SliverGridDelegateWithFixedCrossAxisCount,根据类名我们也能大概猜它是干什么用的:如果你的布局中每一行的列数是固定的,那你就应该用它。 来看下其构造函数: SliverGridDelegateWithFixedCrossAxisCount({ @required this.crossAxisCount, this.mainAxisSpacing = 0.0, this.crossAxisSpacing = 0.0, this.childAspectRatio = 1.0,})crossAxisCount:列数,即一行有几个子元素;mainAxisSpacing:主轴方向上的空隙间距;crossAxisSpacing:次轴方向上的空隙间距;childAspectRatio:子元素的宽高比例。 想必看到上面的示例图,你就秒懂其中各个参数的含义了。不过,这里有一点需要特别注意:如果你的子元素宽高比例不为1,那么你一定要设置childAspectRatio属性。 2.2 SliverGridDelegateWithMaxCrossAxisExtentSliverGridDelegateWithMaxCrossAxisExtent在实际应用中可能会比较少,来看下其构造函数: SliverGridDelegateWithMaxCrossAxisExtent({ @required this.maxCrossAxisExtent, this.mainAxisSpacing = 0.0, this.crossAxisSpacing = 0.0, this.childAspectRatio = 1.0,})可以看到除了maxCrossAxisExtent外,其他参数和SliverGridDelegateWithFixedCrossAxisCount都是一样的。那么maxCrossAxisExtent是干什么的呢?我们来看个例子: ...

July 15, 2019 · 1 min · jiezi

在Flutter中使用自定义Icon

1. 前言Flutter作为时下最流行的技术之一,凭借其出色的性能以及抹平多端的差异优势,早已引起大批技术爱好者的关注,甚至一些闲鱼,美团,腾讯等大公司均已投入生产使用。虽然目前其生态还没有完全成熟,但身靠背后的Google加持,其发展速度已经足够惊人,可以预见将来对Flutter开发人员的需求也会随之增长。 无论是为了技术尝鲜还是以后可能的工作机会,都9102年了,作为一个前端开发者,似乎没有理由不去尝试它。正是带着这样的心理,笔者也开始学习Flutter,同时建了一个用于练习的仓库,后续所有代码都会托管在上面,欢迎star,一起学习。 今天要分享的内容其实非常简单,我们都知道Flutter内置了一套Material Design风格的Icon图标,但对于一个成熟的App而言,通常情况下还是远远不够的。为此,我们需要在项目中引入自定义的Icon图标。 本文就将以Ant Design图标库为例,介绍如何在Flutter中引入自定义图标。 2. 准备工作:字体文件正所谓“巧妇难为无米之炊”,要想引入自定义图标,首先我们得准备好图标字体文件(.ttf后缀)。对于大公司而言,找视觉同学切就可以了。但如果是自己做的业余项目或者没有资源的时候,我们可以上阿里巴巴矢量图标库pick自己心仪的图标。 这里就以Ant Design官方图标库为例(一共有600个图标),通过以下操作,我们将图标字体文件加入到项目中: 添加购物车 --> 点击购物车 --> 下载代码 --> 解压 --> 拷贝至项目(可重命名) 3. 声明自定义字体仅仅将字体文件复制到项目中还不够,我们需要通过声明的方式来告诉Flutter有新字体可用。打开项目根目录下的pubspec.yaml文件,找到fonts这一段: To add custom fonts to your application, add a fonts section here, in this "flutter" section. Each entry in this list should have a "family" key with the font family name, and a "fonts" key with a list giving the asset and other descriptors for the font.注释就是让我们在该段文字下方添加自定义字体的声明,结合其注释掉的例子和当前的项目目录,我们可以这样配置: ...

July 15, 2019 · 1 min · jiezi

UI2CODE复杂背景无法识别闲鱼工程师这样打造高准确率方案

复杂背景内容提取指的是从复杂的背景中提取出特定的内容,例如在图片中提取特定的文字,在图片中提取特定的叠加图层等等。这是一个业界难题,基于传统的图像处理的方法存在准确率和召回率的问题,没法解决语义的问题。而主流的机器学习的方法,例如目标检测无法获取像素级别的位置信息,而语义分割的方法则只能提取像素而无法获取半透明叠加前的像素信息。本文考虑到这些痛点,从UI2CODE业务的业务场景出发,采用了目标检测网络来实现内容召回,GAN网络实现复杂背景中特定前景内容的提取和复原。 处理流程:复杂背景的处理流程分为如下几个步骤: 内容召回:通过目标检测网络召回元素,即元素是否需要做背景提取操作。区域判断:根据梯度等视觉方法判断所处区域是否是复杂区域。简单区域:基于梯度的方式找到背景区块。复杂区域:采用SRGAN网络进行内容提取。内容召回:内容召回我们采用目标检测网络来实现,例如Faster-rcnn或者Mask-rcnn等,如下图所示: 区域判断:根据拉普拉斯算子计算周边梯度,判断所处区域是否是复杂区域。 简单背景:由于目标检测模型本身的局限性,会导致没法达到像素级别的精确性,因此需要对位置做修正。如果是简单背景就可以基于梯度的思想做位置修正,具体计算方式如下: 复杂背景:背景是复杂背景时,左图是原图,右图是提取的文字区块: 此时提取出的框不是完全正确,那么此时根据梯度等机器视觉算法已经不能对位置做正确的修正了。本文提出了基于GAN网络的方式来解决复杂背景内容提取问题,网络的主要结构如下图所示: 为什么选择GAN网络?1)基于srGAN网络,该网络加入了特征图的损失函数,这样可以很好保留高频信息,能更好的保留边缘。特征图的损失函数如下图所示: 2)由于有对抗损失的存在,可以很好的降低误检率。 3)最重要的一点是在有透明度的场景下,语义分割网络只能“提取”元素,无法“还原”元素。而GAN网络不仅可以在提取元素的同时还原出未叠加时的像素情况。 网络训练流程图 针对业务场景对GAN网络做的改进1.由于我们不是超分辨率场景,因此不用pixelShuffler模块做上采样 2.由于场景比较复杂,可以引入denseNet和加深网络来提高准确率。 3.内容损失函数对于压制误判的噪点效果不理想,因此加大了误判的惩罚,具体如下图所示: 预测获取的结果图I: 预测获取的结果图II: 结束语本篇我们通过复杂背景内容提取的介绍,提出了一种机器学习为主,图像处理为辅去精确获取特定前景内容的方法,得到了高精确率、高召回率和高定位精度的识别结果。下图分别是传统算法grabcut,语义分割方法deeplab和本文方法的各个指标的情况。 经过数据论证,我们发现了一个值得进一步优化的点——需要大量样本适配不同的特征尺度,这里的投入会相对较大。如何进一步提高打标效率呢,我们将会在后续系列文章中和大家分享。 本文作者:仝辉,深宇阅读原文 本文为云栖社区原创内容,未经允许不得转载。

July 10, 2019 · 1 min · jiezi

Flutter滚动型容器组件-ListView篇

1. 前言Flutter作为时下最流行的技术之一,凭借其出色的性能以及抹平多端的差异优势,早已引起大批技术爱好者的关注,甚至一些闲鱼,美团,腾讯等大公司均已投入生产使用。虽然目前其生态还没有完全成熟,但身靠背后的Google加持,其发展速度已经足够惊人,可以预见将来对Flutter开发人员的需求也会随之增长。 无论是为了技术尝鲜还是以后可能的工作机会,都9102年了,作为一个前端开发者,似乎没有理由不去尝试它。正是带着这样的心理,笔者也开始学习Flutter,同时建了一个用于练习的仓库,后续所有代码都会托管在上面,欢迎star,一起学习。 在上一篇文章中,我们学习了Flutter中使用频率最高的一些基础组件。但是在一些场景中,当组件的宽度或高度超出屏幕边缘时,Flutter往往会给出overflow警告,提醒有组件溢出屏幕。为了解决这个问题,今天我们就来学习最常用的一个滚动型容器组件 — ListView组件。 2. ListView使用方法从功能比较上来看,Flutter中的ListView组件和RN中的ScrollView/FlatList非常相似,但是在使用方法上还是有点区别。接下来,就跟着我一起来看看ListView组件都有哪些常用的使用方法。 2.1 ListView()第一种使用方法就是直接调用其默认构造函数来创建列表,效果等同于RN中的ScrollView组件。但是这种方式创建的列表存在一个问题:对于那些长列表或者需要较昂贵渲染开销的子组件,即使还没有出现在屏幕中但仍然会被ListView所创建,这将是一项较大的开销,使用不当可能引起性能问题甚至卡顿。 不过话说回来,虽然该方法可能会有性能问题,但还是取决于其不同的应用场景而定。下面我们就来看下其构造函数(已省略不常用属性): ListView({ Axis scrollDirection = Axis.vertical, ScrollController controller, ScrollPhysics physics, bool shrinkWrap = false, EdgeInsetsGeometry padding, this.itemExtent, double cacheExtent, List<Widget> children = const <Widget>[],})scrollDirection: 列表的滚动方向,可选值有Axis的horizontal和vertical,可以看到默认是垂直方向上滚动;controller : 控制器,与列表滚动相关,比如监听列表的滚动事件;physics: 列表滚动至边缘后继续拖动的物理效果,Android与iOS效果不同。Android会呈现出一个波纹状(对应ClampingScrollPhysics),而iOS上有一个回弹的弹性效果(对应BouncingScrollPhysics)。如果你想不同的平台上呈现各自的效果可以使用AlwaysScrollableScrollPhysics,它会根据不同平台自动选用各自的物理效果。如果你想禁用在边缘的拖动效果,那可以使用NeverScrollableScrollPhysics;shrinkWrap: 该属性将决定列表的长度是否仅包裹其内容的长度。当ListView嵌在一个无限长的容器组件中时,shrinkWrap必须为true,否则Flutter会给出警告;padding: 列表内边距;itemExtent: 子元素长度。当列表中的每一项长度是固定的情况下可以指定该值,有助于提高列表的性能(因为它可以帮助ListView在未实际渲染子元素之前就计算出每一项元素的位置);cacheExtent: 预渲染区域长度,ListView会在其可视区域的两边留一个cacheExtent长度的区域作为预渲染区域(对于ListView.build或ListView.seperated构造函数创建的列表,不在可视区域和预渲染区域内的子元素不会被创建或会被销毁);children: 容纳子元素的组件数组。上面的属性介绍一大堆,都不如一个实际例子来得实在。我们可以用一个ListView组件来包裹上篇文章中实现的银行卡,宠物卡片,朋友圈这三个例子: 代码(文件地址) class NormalList extends StatelessWidget { const NormalList({Key key}) : super(key: key); @override Widget build(BuildContext context) { return ListView( children: <Widget>[ CreditCard(data: creditCardData), PetCard(data: petCardData), FriendCircle(data: friendCircleData), ], ); }}预览 ...

July 10, 2019 · 3 min · jiezi

用Flutter构建漂亮的UI界面-基础组件篇

1. 前言Flutter作为时下最流行的技术之一,凭借其出色的性能以及抹平多端的差异优势,早已引起大批技术爱好者的关注,甚至一些闲鱼,美团,腾讯等大公司均已开始使用。虽然目前其生态还没有完全成熟,但身靠背后的Google加持,其发展速度已经足够惊人,可以预见将来对Flutter开发人员的需求也会随之增长。 无论是为了现在的技术尝鲜还是将来的潮流趋势,都9102年了,作为一个前端开发者,似乎没有理由不去尝试它。正是带着这样的心理,笔者也开始学习Flutter,同时建了一个用于练习的仓库,后续所有代码都会托管在上面,欢迎star,一起学习。 今天分享的是Flutter中最常用到的一些基础组件,它们是构成UI界面的基础元素:容器,行,列,绝对定位布局,文本,图片和图标等。 2. 基础组件2.1 Container(容器组件) Container组件是最常用的布局组件之一,可以认为它是web开发中的div,rn开发中的View。其往往可以用来控制大小、背景颜色、边框、阴影、内外边距和内容排列方式等。我们先来看下其构造函数: Container({ Key key, double width, double height, this.margin, this.padding, Color color, this.alignment, BoxConstraints constraints, Decoration decoration, this.foregroundDecoration, this.transform, this.child,})2.1.1 width,height,margin,padding这些属性的含义和我们已经熟知的并没有区别。唯一需要注意的是,margin和padding的赋值不是一个简单的数字,因为其有left, top, right, bottom四个方向的值需要设置。Flutter提供了EdgeInsets这个类,帮助我们方便地生成四个方向的值。通常情况下,我们可能会用到EdgeInsets的4种构造方法: EdgeInsets.all(value): 用于设置4个方向一样的值;EdgeInsets.only(left: val1, top: val2, right: val3, bottom: val4): 可以单独设置某个方向的值;EdgeInsets.symmetric(horizontal: val1, vertical: val2): 用于设置水平/垂直方向上的值;EdgeInsets.fromLTRB(left, top, right, bottom): 按照左上右下的顺序设置4个方向的值。2.1.2 color该属性的含义是背景颜色,等同于web/rn中的backgroundColor。需要注意的是Flutter中有一个专门表示颜色的Color类,而非我们常用的字符串。不过我们可以非常轻松地进行转换,举个栗子: 在web/rn中我们会用'#FF0000'或'red'来表示红色,而在Flutter中,我们可以用Color(0xFFFF0000)或Colors.red来表示。 2.1.3 alignment该属性是用来决定Container组件的子组件将以何种方式进行排列(PS:再也不用为怎么居中操心了)。其可选值通常会用到: Alignment.topLeft: 左上Alignment.topCenter: 上中Alignment.topRight: 右上Alignment.centerLeft: 左中Alignment.center: 居中Alignment.centerRight: 右中Alignment.bottomLeft: 左下Alignment.bottomCenter: 下中Alignment.bottomRight: 右下2.1.4 constraints在web/rn中我们通常会用minWidth/maxWidth/minHeight/maxHeight等属性来限制容器的宽高。在Flutter中,你需要使用BoxConstraints(盒约束)来实现该功能。 // 容器的大小将被限制在[100*100 ~ 200*200]内BoxConstraints( minWidth: 100, maxWidth: 200, minHeight: 100, maxHeight: 200,)2.1.5 decoration该属性非常强大,字面意思是装饰,因为通过它你可以设置边框,阴影,渐变,圆角等常用属性。BoxDecoration继承自Decoration类,因此我们通常会生成一个BoxDecoration实例来设置这些属性。 ...

July 8, 2019 · 5 min · jiezi

有选择性的启用SAP-UI5调试版本的源代码

在低版本的SAP UI5应用中,我们一旦切换成调试模式,那么应用程序源代码和UI5框架程序的源代码的调试版本都会重新加载,耗时很长。 我最近发现UI5新版本1.66.1提供了选择性加载调试版本的源代码的选项,即下图中的Select Specific modules: 如果确认问题出在我们应用程序,只想调试自己编写的应用代码,那么我们可以只切换应用程序成为调试版本,这样速度大大提高。 此时浏览器地址栏里看到的参数为sap-ui-debug=dis/#, 意思是仅仅disnamespace下的所有资源加载成调试版本: 调试速度大大提高: 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 28, 2019 · 1 min · jiezi

SAP-WebIDE里UI5应用的隐藏文件projectjson

在SAP WebIDE UI5应用编辑器里的菜单View->Show Hidden files点击后,即可发现项目文件夹下有一个隐藏文件project.json: 内容如下: 这也解释了为什么build之后,UI5应用文件夹下会多出一个dist的文件夹,内容和webapp里的代码几乎一致: databinding节点下罗列出了每个视图绑定的OData数据集的节点名称,非常实用: generation节点记录了应用创建的时间戳和基于的Fiori模板ID和版本号,translation包含了和翻译相关的资源名称。 basevalidator维护了SAP WebIDE提供的校验实现,当前有fioriXmlAnalysis和fioriJsValidator。 codeCheckingTriggers定义了校验出的错误是否会阻止UI5实际部署到SAP云平台上。hcpdeploy包含了和UI5应用部署相关的信息,比如SAP Cloud Platform的用户id,和部署到云平台之后的应用名称。 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 27, 2019 · 1 min · jiezi

SAP-UI5的support-Assistant

SAP UI5的support Assistant给UI5刚入门的开发人员提供了一种极便利的快速熟悉UI5代码的途径。 召唤方式: ctrl+shift+alt+p四个键同时按,在弹出的对话框里点击按钮“activate Support Assistant即可。 之后屏幕下方会出现一个视图,视图左边包含了SAP预定义的检查条目,以及您当前的UI5应用基于这些检查条目校验出的结果。右边则是检查条目检查的具体内容,针对检查出来的问题给出的解决方案,以及具体的检查代码等等。 我觉得对我自己帮助最大的就是Code标签里每个检查条目的具体实现,能让我学习到很多SAP UI5的编程细节。 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 27, 2019 · 1 min · jiezi

XUI-一个简洁而又优雅的Android原生UI框架

XUI 一个简洁而又优雅的Android原生UI框架,解放你的双手!还不赶紧点击使用说明文档,体验一下吧! 涵盖绝大部分的UI组件:TextView、Button、EditText、ImageView、Spinner、Picker、Dialog、PopupWindow、ProgressBar、LoadingView、StateLayout、FlowLayout、Switch、Actionbar、TabBar、Banner、GuideView、BadgeView、MarqueeView、WebView、SearchView等一系列的组件和丰富多彩的样式主题。在提issue前,请先阅读【提问的智慧】,并严格按照issue模板进行填写,节约大家的时间。 关于我 特征简洁优雅,尽可能少得引用资源文件的数量,项目库整体大小不足1M(打包后大约644k)组件丰富,提供了绝大多数我们在开发者常用的功能组件。使用简单,为方便快速开发,提高开发效率,对api进行了优化,提供一键式接入。样式统一,框架提供了一系列统一的样式,使UI整体看上去美观和谐。兼容性高,框架还提供了3种不同尺寸设备的样式(4.5英寸、7英寸和10英寸),并且最低兼容到Android 17, 让UI兼容性更强。扩展性强,各组件提供了丰富的属性和样式API,可以通过设置不同的样式属性,构建不同风格的UI。如何使用添加Gradle依赖1.先在项目根目录的 build.gradle 的 repositories 添加: allprojects { repositories { ... maven { url "https://jitpack.io" } }}2.然后在dependencies添加: dependencies { ... //1.0.5版本后只支持androidx implementation 'com.github.xuexiangjys:XUI:1.0.5' implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.recyclerview:recyclerview:1.0.0' implementation 'com.google.android.material:material:1.1.0-alpha07' implementation 'com.github.bumptech.glide:glide:4.8.0'}【注意】如果你的项目目前还未使用androidx,那你只能使用1.0.5之前的版本了。 dependencies { ... implementation 'com.github.xuexiangjys:XUI:1.0.4' implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support:recyclerview-v7:28.0.0' implementation 'com.android.support:design:28.0.0' implementation 'com.github.bumptech.glide:glide:4.8.0'}初始化XUI设置1.在Application最顶部初始化设置 XUI.init(this); //初始化UI框架XUI.debug(true); //开启UI框架调试日志2.调整应用的基础主题 必须设置应用的基础主题,否则组件将无法正常使用!基础主题类型: 大平板(10英寸, 240dpi, 1920*1200):XUITheme.Tablet.Big小平板(7英寸, 320dpi, 1920*1200):XUITheme.Tablet.Small手机(4.5英寸, 320dpi, 720*1280):XUITheme.Phone<style name="AppTheme" parent="XUITheme.Phone">
<!-- 自定义自己的主题样式 -->
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item>
</style>当然也可以在Activity刚开始时调用如下代码动态设置主题 ...

June 14, 2019 · 1 min · jiezi

Android-Studio-内建立适配折叠屏的虚拟机

目前,本人能找到创造折叠屏虚拟机有如下两种方案。1、用android-studio内置的虚拟机,不过要下载canary版本的android-studio。 在canary10的版本中,安卓官方就已经出了折叠屏模拟器。 官方教程如下:https://androidstudio.googleb... 2、用三星的折叠屏模拟器。该方法比较简单,不用下载canary版本的android-studio,只需要建立一个平板模拟器即可。但安卓的镜像得是x86_64的(因为要安装一个三星模拟器的APP)。 然后安装一个名字为:FoldableEmulator_1.01.apk。的APP。 官方教程如下:https://developer.samsung.com...

June 11, 2019 · 1 min · jiezi

如何查找SAP-Fiori-launchpad-Designer的准确路径即url地址

比如我们知道在SPRO里下面这个路径的customizing activity里打开Fiori Launchpad designer: SAP Netweaver->UI technologies->SAP Fiori->Configure Launchpad content->Adding Apps to SAP Fiori Launchpad->Configure Target Mappings and Tiles->SAP Fiori Launchpad Designer, 点右键,选择Display technical info: 找到这个activity绑定的事务码为/UI2/FLPD_CUST: SE93里查到这个事务码绑定的report名称为/UI2/START_URL: 这个报表执行后的界面: Fiori Launchpad designer的准确路径可以通过调试获得,存储于变量gv_url里: 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

June 1, 2019 · 1 min · jiezi

码农如何学设计-Web-UI-设计学习心得

我的本职工作是后端开发工程师,记不清从什么时候开始对「设计」产生了兴趣,可是对「设计」领域知识的学习由于从小就受到固有思维的影响,甚至受到很多不正确教学方法的误导,在学习的过程中很容易让人产生自我怀疑,特别是对天赋的自我怀疑,从而慢慢的主动放弃。 本文结合自己的学习经历,从「什么是设计」开始切入,引出何选择设计工具,如何画下设计的第一笔以及随之而来的其他基础设计问题。希望这篇关于学习设计最初级的心得体会能给那些曾经跟我有类似兴趣的朋友们一些学习上的参考。 如果想成为一名高级的、或者艺术家级别的设计师肯定需要天赋。但是,对于大多数为了完成工作或者实现自己想法的基本设计能力完全可以通过自学学会的。 什么是设计?“设计是努力使现况接近期望的探索过程”,- by 著名建筑设计师:姚仁禄。与百科词条中对于“设计”的解释相比,我更喜欢姚先生的版本,言简意赅,直击重点。 好的思想都是相通的,不管是建筑领域还是互联网领域,对我们学习 Web UI 设计同样受用。设计并没有标准答案,通常也不是一步到位,好的设计作品大多是不断演化而成的。因此先认清现况是一个很有必要的步骤,能够帮助我们制定出更合理的期望/目标,避免好高骛远。 本文以「如何学习 Web UI 设计」为例子: 假定我们的现况/水平:新手制定出具体的期望/目标:设计一个博客网站最后,也是最重要的过程就是:付出实际行动,一步步将这个目标实现如何选择设计工具上一步已经确定了目标,下一个随之而来的问题就是:选择什么样的设计工具? 我很喜欢”随之而来“这个词,有些东西不需要刻意,只要你把当下想清楚之后,未来会主动找上门。设计工具有很多,Photoshop,Sketch,Adobe XD 等等甚至还可以直接用纸笔。 对于初学者的建议是:以最快的速度确定一款设计工具,哪怕是通过点兵点将、扔筛子的方式,重点是快速做出选择,然后付出行动,动手操作。 初学设计的时候我也很纠结,看了很多工具对比文章,甚至去看国外分享的工具对比视频。但是,当我真正开始动手去做一个实实在在作品的时候,发现设计的思想才是重点,工具之间无非就是有些软件对某些操作比较方便而已,等你亲自动手做过一次完完整整的设计作品之后就会发现,即使下一次做设计想换到其他工具也是很容易上手的。 如何在空白页面画下第一笔我们将要设计的博客网站最终需要在浏览器上显示,由于显示器屏幕有非常多的规格大小,小到手机上的浏览器、主流桌面浏览器、大到接近电视的超宽屏显示器 面对这么多的载体,会有很多问题随之而来: 如何让我们的网页能够在不同尺寸上都有比较理想的显示效果?页面上的内容应该居中对齐,还是左对齐,右对齐?如果是左对齐,应该距离左边多少?如果是右对齐,应该距离右边多少?...... 等等这些问题可以通过开启网格辅助线的方式解决,辅助我们做设计 随之而来的其他问题有了网格系统辅助我们做设计,可以大胆的画下第一笔。 可是,第一笔之后又有很多问题随之而来: 如果你第一笔写的文字,文字的字体、字号、颜色等等问题随之而来 如果你第一笔画的是形状,形状边框样式,背景颜色,阴影等等问题也会随之而来 一旦你开始第二笔,两笔元素之间的间距、对齐方式等等问题立刻紧随其后 这些问题是新手做设计最头疼的问题,是最容易产生自我怀疑的设计阶段。我在自学设计的过程中,也是参考了无数资料,做了大量练习,总体的解决思路无非下面一张图所示: 以颜色为例: 如果你有经验,这里说的有经验包括以下几种情况 不管经验是来自以往的设计经验,还是衣服搭配等等,只要你可以确定出自己最想用的颜色,就可以尝试用这种颜色及其衍生色做设计中的主要颜色公司的产品已经有品牌色来自其他的,任何可以让你不需要纠结就能确定出颜色的都属于有经验如果你没有任何经验 从现在开始,养成一个习惯:将平时看到的,自己喜欢的网站,收藏起来。收集资源是一个长期的过程,不一定等到要用了才去找从上一步收集的资源中,挑选出你最想要的配色或者变体色如果你不想参考任何其他网站的配色,那就用最简单的白底黑字去设计,但是记住一点:并不是只有纯净的黑与白,黑白之间还有很多不同等级的灰度色可以使用 动手操作设计是一门需要持续动手实践的技能,意淫多少篇都不如亲自动手实操一遍来的实在。 当你真正亲自动手做一次设计之后就会发现,哪怕只是设计一个简单的博客网站,也会遇到设计过程中90%的常见问题,而且这些问题跟你选择什么样的设计工具没有任何关系,重点在思想。有了想法之后,通过不断练习丰富自己的经验,同时完善自己的设计思想。 我已经将文章开头定下的「设计一个博客网站」的目标实现了,点击下方的链接查看完整实现过程 从 0 开始学设计、并将设计稿转成 HTML + CSS + JS 同时学习「设计」与「前端开发」基本技能

May 22, 2019 · 1 min · jiezi

Material-Design-组件之NavigationView

原文首发于微信公众号:jzman-blog,欢迎关注交流!Material Design 系列文章: Material Design组件之FloatingActionButtonMaterial Design组件之AppBarLayoutMaterial Design组件之CollapsingToolbarLayout今天来看一下 NavigationView 的使用,NavigationView 是一个标准的导航菜单,其菜单内容由菜单资源文件来填充,NavigationView 一般和 DrawerLayout 一起搭配使用构成抽屉菜单,分别由内容页和菜单页组成。主要内容如下: 基本布局常用属性文字选中效果图标与文字间距案例显示效果基本布局可以直接使用 DrawerLayout 作为根布局,里面依次是内容布局和菜单布局,切记内容布局一定是在菜单布局的前面,可以这样理解菜单划出的时候肯定应该在内容布局之上,如果两者顺序相反,则影响菜单 Item 的点击事件以及菜单的滑动隐藏,当然如果有 ToolBar 等,可以按需添加到内容布局中,也可以 DrawerLayout 外,唯一区别是侧换菜单是否遮挡 ToolBar,基本使用参考如下: <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <!--内容 --> <!--菜单--> <android.support.design.widget.NavigationView android:id="@+id/navigation" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" app:menu="@menu/my_navigation_items" /> </android.support.v4.widget.DrawerLayout>常用属性下面是 NavigationView 的常用属性,具体如下: <!--菜单弹出方向-->android:layout_gravity="start"<!--菜单图标渲染的颜色-->app:itemIconTint="@color/colorPrimary"<!--菜单文字的颜色-->app:itemTextColor="@color/colorNormal"<!--菜单项背景颜色(组之间有间隔)-->app:itemBackground="@color/colorBackground"<!--菜单项-->app:menu="@menu/menu_navigation_view" <!--NavigationView的头部布局-->app:headerLayout="@layout/head_navigation_layout"文字选中效果如果美工比较用心会告诉点击时是那种颜色、按下是那种颜色或者是某种效果,此时就需要设置菜单项文字选中效果了,这里选择创建在 color 资源文件的形式来实现文字选中效果了,定义 color 资源文件如下: <?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <!--按下--> <item android:color="@color/colorPress" android:state_pressed="true"/> <!--选中--> <item android:color="@color/colorCheck" android:state_checked="true"/> <!--默认--> <item android:color="@color/colorNormal"/></selector>然后,设置 NavigationView 的 itemTextColor 属性即可,具体如下: ...

May 15, 2019 · 2 min · jiezi

基本功 | Litho的使用及原理剖析

什么是Litho?Litho是Facebook推出的一套高效构建Android UI的声明式框架,主要目的是提升RecyclerView复杂列表的滑动性能和降低内存占用。下面是Litho官网的介绍:Litho is a declarative framework for building efficient user interfaces (UI) on Android. It allows you to write highly-optimized Android views through a simple functional API based on Java annotations. It was primarily built to implement complex scrollable UIs based on RecyclerView.With Litho, you build your UI in terms of components instead of interacting directly with traditional Android views. A component is essentially a function that takes immutable inputs, called props, and returns a component hierarchy describing your user interface.Litho是高效构建Android UI的声明式框架,通过注解API创建高优的Android视图,非常适用于基于Recyclerview的复杂滚动列表。Litho使用一系列组件构建视图,代替了Android传统视图交互方式。组件本质上是一个函数,它接受名为Props的不可变输入,并返回描述用户界面的组件层次结构。Litho是一套完全不同于传统Android的UI框架,它继承了Facebook一向大胆创新的风格,突破性地在Android上实现了React风格的UI框架。架构图如下:应用层:上层Android应用接入层。规范层(API):允许用户使用声明式的API(注解)来构建符合Flexbox规范的布局。布局层:Litho使用可挂载组件、布局组件和Flexbox组件来构建布局,其中可挂载组件和布局组件允许用户使用规范来定义,各个组件的具体用法下面的组件规范中会详细介绍。在Litho中每一个组件都是一个独立的功能模块。Litho的组件和React的组件相类似,也具有属性和状态的概念,通过状态的变更来控制组件的展示样式。布局测量:Litho使用Yoga来完成组件布局的异步或同步(可根据场景定制)测量和计算,实现了布局的扁平化。布局渲染:Litho不仅支持使用View来渲染视图,还可以使用更轻量的Drawable来渲染视图。Litho实现了大量使用Drawable来渲染的基础组件,可以进一步拍平布局。除了上面提到的扁平化布局,Litho还实现了布局的细粒度复用和异步计算布局的能力,对于这些功能的实现在Litho的特性及原理剖析中详细介绍。下面先介绍一下大家比较关心的Litho使用方法。2. Litho的使用Litho的使用方式相比于传统的Android来说有些另类,它抛弃了通过XML定义布局的方式,采用声明式的组件在Java中构建布局。2.1 Litho和原生Android在使用上的区别Android传统布局:首先在资源文件res/layout目录下定义布局文件xx.xml,然后在Activity或Fragment中引用布局文件生成视图,示例如下:<?xml version=“1.0” encoding=“utf-8”?><TextView xmlns:android=“http://schemas.android.com/apk/res/android" android:layout_width=“wrap_content” android:layout_height=“wrap_content” android:text=“Hello World” android:textAlignment=“center” android:textColor="#666666” android:textSize=“40dp” />public class MainActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.helloworld); }}Litho布局:Litho抛弃了Android原生的布局方式,通过组件方式构建布局生成视图,示例如下:public class MainActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ComponentContext context = new ComponentContext(this); final Text.Builder builder = Text.create(context); final Component = builder.text(“Hello World”) .textSizeDip(40) .textColor(Color.parseColor("#666666")) .textAlignment(Layout.Alignment.ALIGN_CENTER) .build(); LithoView view = LithoView.create(context, component); setContentView(view); }}2.2 Litho自定义视图Litho中的视图单元叫做Component,可以直观的翻译为“组件”,它的设计理念来自于React组件化的思想。每个组件持有描述一个视图单元所必须的属性和状态,用于视图布局的计算工作。视图最终的绘制工作是由组件指定的绘制单元(View或者Drawable)来完成的。Litho组件的创建方式也和原生View的创建方式有着很大的区别。Litho使用注解定义了一系列的规范,我们需要使用Litho的注解来定义自己的组件生成规则,最终由Litho在编译期自动编译生成真正的组件。2.2.1 组件规范Litho提供了两种类型的组件规范,分别是Layout Spec规范和Mount Spec规范。下面分别介绍两种规范的使用方式:Layout Spec规范:用于生成布局类型组件的规范,布局组件在逻辑上等同于Android中的ViewGroup,用于组织其他组件构成一个布局。它要求我们必须使用@LayoutSpec注解来注明,并实现一个标注了@OnCreateLayout注解的方法。示例如下:@LayoutSpecclass HelloComponentSpec { @OnCreateLayout static Component onCreateLayout(ComponentContext c, @Prop String name) { return Column.create(c) .child(Text.create(c) .text(“Hello, " + name) .textSizeRes(R.dimen.my_text_size) .textColor(Color.BLACK) .paddingDip(ALL, 10) .build()) .child(Image.create(c) .drawableRes(R.drawable.welcome) .scaleType(ImageView.ScaleType.CENTER_CROP) .build()) .build(); }}最终Litho会在编译时生成一个名为HelloComponent的组件。public final class HelloComponent extends Component { @Prop(resType = ResType.NONE,optional = false) String name; private HelloComponent() { super(); } @Override protected Component onCreateLayout(ComponentContext c) { return (Component) HelloComponentSpec.onCreateLayout((ComponentContext) c, (String) name); } … public static Builder create(ComponentContext context, int defStyleAttr, int defStyleRes) { Builder builder = sBuilderPool.acquire(); if (builder == null) { builder = new Builder(); } HelloComponent instance = new HelloComponent(); builder.init(context, defStyleAttr, defStyleRes, instance); return builder; } public static class Builder extends Component.Builder<Builder> { private static final String[] REQUIRED_PROPS_NAMES = new String[] {“name”}; private static final int REQUIRED_PROPS_COUNT = 1; HelloComponent mHelloComponent; … public Builder name(String name) { this.mHelloComponent.name = name; mRequired.set(0); return this; } @Override public HelloComponent build() { checkArgs(REQUIRED_PROPS_COUNT, mRequired, REQUIRED_PROPS_NAMES); HelloComponent helloComponentRef = mHelloComponent; release(); return helloComponentRef; } }}Mount Spec规范:用来生成可挂载类型组件的规范,用来生成渲染具体View或者Drawable的组件。同样,它必须使用@MountSpec注解来标注,并至少实现一个标注了@onCreateMountContent的方法。Mount Spec相比于Layout Spec更复杂一些,它拥有自己的生命周期:@OnPrepare,准备阶段,进行一些初始化操作。@OnMeasure,负责布局的计算。@OnBoundsDefined,在布局计算完成后挂载视图前做一些操作。@OnCreateMountContent,创建需要挂载的视图。@OnMount,挂载视图,完成布局相关的设置。@OnBind,绑定视图,完成数据和视图的绑定。@OnUnBind,解绑视图,主要用于重置视图的数据相关的属性,防止出现复用问题。@OnUnmount,卸载视图,主要用于重置视图的布局相关的属性,防止出现复用问题。除了上述两种组件类型,Litho中还有一种特殊的组件——Layout,它不能使用规范来生成。Layout是Litho中的容器组件,类似于Android中的ViewGroup,但是只能使用Flexbox的规范。它可以包含子组件节点,是Litho各组件连接的纽带。Layout组件只是Yoga在Litho中的代理,组件的所有布局相关的属性都会直接设置给Yoga,并由Yoga完成布局的计算。Litho实现了两个Layout组件Row和Column,分别对应Flexbox中的行和列。2.2.2 Litho的属性在Litho中属性分为两种,不可变属性称为Props,可变属性称为State,下面分别介绍一下两种属性:Props属性:组件中使用@Prop注解标注的参数集合,具有单向性和不可变性。下面通过一个简单的例子了解一下如何在组件中定义和使用Props属性: @MountSpec class MyComponentSpec { @OnPrepare static void onPrepare( ComponentContext c, @Prop(optional = true) String prop1) { … } @OnMount static void onMount( ComponentContext c, SomeDrawable convertDrawable, @Prop(optional = true) String prop1, @Prop int prop2) { if (prop1 != null) { … } } }在上面的代码中,共使用了三次Prop注解,分别标注prop1和prop2两个变量,即定义了prop1和prop2两个属性。Litho会在自动编译生成的MyComponent类的Builder类中生成这两个属性的同名方法。按照如下代码,便可以去使用上面定义的属性: MyComponent.create(c) .prop1(“My prop 1”) .prop2(256) .build();State属性:意为“状态”属性,State属性虽然可变,但是其变化由组件内部控制,例如:输入框、Checkbox等都是由组件内部去感知用户行为,并更新组件的State属性。所以一个组件一旦创建,我们便无法通过任何外部设置去更改它的属性。组件的State属性虽然不允许像Props属性那样去显式设置,但是我们可以定义一个单独的Props属性来当做某个State属性的初始值。3. Litho的特性及原理剖析Litho官网首页通过4个段落重点介绍了Litho的4个特性。3.1 声明式组件Litho采用声明式的API来定义UI组件,组件通过一组不可变的属性来描述UI。这种组件化的思想灵感来源于React,关于声明式组件的用法上面已经详细介绍过了。传统Android布局因为UI与逻辑分离,所以开发工具都有强大的预览功能,方便开发者调整布局。而Litho采用React组件化的思想,通过组件连接了逻辑与布局UI,虽然Litho也提供了对Stetho的支持,借助于Chrome开发者工具对界面进行调试,不过使用起来并没有那么方便。3.2 异步布局Android系统在绘制时为了防止页面错乱,页面所有View的测量(Measure)、布局(Layout)以及绘制(Draw)都是在UI线程中完成的。当页面UI非常复杂、视图层级较深时,难免Measure和Layout的时间会过长,从而导致页面渲染时候丢帧出现卡顿情况。Litho为解决该问题,提出了异步布局的思想,利用CPU的闲置时间提前在异步线程中完成Measure和Layout的过程,仅在UI线程中完成绘制工作。当然,Litho只是提供了异步布局的能力,它主要使用在RecyclerView等可以提前知道下一个视图长什么样子的场景。3.2.1 异步布局原理剖析针对RecyclerView等滑动列表,由于可以提前知道接下来要展示的一个甚至多个条目的视图样式,所以只要提前创建好下一个或多个条目的视图,就可以提前完成视图的布局工作。那么Android原生为什么不支持异步布局呢?主要有以下两个原因:View的属性是可变的,只要属性发生变化就可能导致布局变化,因此需要重新计算布局,那么提前计算布局的意义就不大了。而Litho组件的属性是不可变的,所以对于一个组件来说,它的布局计算结果是唯一且不变的。提前异步布局就意味着要提前创建好接下来要用到的一个或者多个条目的视图,而Android原生的View作为视图单元,不仅包含一个视图的所有属性,而且还负责视图的绘制工作。如果要在绘制前提前去计算布局,就需要预先去持有大量未展示的View实例,大大增加内存占用。反观Litho的组件则没有这个问题,Litho的组件只是视图属性的一个集合,仅负责计算布局,绘制工作由指定的绘制单元来完成,相比与传统的View显然Litho的组件要轻量的多。所以在Litho中,提前创建好接下来要用到的多个条目的组件,并不会带来性能问题,甚至还可以直接把组件当成滑动列表的数据源。如下图所示:3.3 扁平化的视图使用Litho布局,我们可以得到一个极致扁平的视图效果。它可以减少渲染时的递归调用,加快渲染速度。下面是同一个视图在Android和Litho实现下的视图层级效果对比。可以看到,同样的样式,使用Litho实现的布局要比使用Android原生实现的布局更加扁平。3.3.1 扁平化视图原理剖析Litho使用Flexbox来创建布局,最终生成带有层级结构的组件树。然后Litho对布局层级进行了两次优化。使用了Yoga来进行布局计算,Yoga会将Flexbox的相对布局转成绝对布局。经过Yoga处理后的布局没有了原来的布局层级,变成了只有一层。虽然不能解决过度绘制的问题,但是可以有效地减少渲染时的递归调用。前面介绍过Litho的视图渲染由绘制单元来完成,绘制单元可以是View或者更加轻量的Drawable,Litho自己实现了一系列挂载Drawable的基本视图组件。通过使用Drawable可以减少内存占用,同时相比于View,Android无法检查出Drawable的视图层级,这样可以使视图效果看起来更加扁平。原理如下图所示,Litho会先把组件树拍平成没有层级的列表,然后使用Drawable来绘制对应的视图单元。Litho使用Drawable代替View能带来多少好处呢?Drawable和View的区别在于前者不能和用户交互,只能展示,因此Drawable不会像View那样持有很多变量和引用,所以Drawable比View从内存上看要轻量很多。举个例子:50个同样展示“Hello world”的TextView和TextDrawable在内存占比上,前者几乎是后者的8倍。对比图如下,Shallow Size表示对象自身占用的内存大小。3.3.2 绘制单元的降级策略由于Drawable不具有交互能力,所以对于使用Drawable无法实现的交互场景,Litho会自动降级成View。主要有以下几种场景:有监听点击事件。限制子视图绘出父布局。有监听焦点变化。有设置Tag。有监听触摸事件。有光影效果。对于以上场景的使用请仔细考虑,过多的使用会导致Litho的层级优化效果变差。3.3.3 对比Android的约束布局为了解决布局嵌套问题,Android推出了约束布局(ConstraintLayout),使用约束布局也可以达到扁平化视图的目的,那么使用Litho的好处是什么呢?Litho可以更好地实现复杂布局。约束布局虽然可以实现扁平效果,但是它使用了大量的约束来固定视图的位置。随着布局复杂程度的增加,约束条件变得越来越多,可读性也变得越来越差。而Litho则是对Flexbox布局进行的扁平化处理,所以实际使用的还是Flexbox布局,对于复杂的布局Flexbox布局可读性更高。3.4 细粒度的复用Litho中的所有组件都可以被回收,并在任何位置进行复用。这种细粒度的复用方式可以极大地提高内存使用率,尤其适用于复杂滑动列表,内存优化非常明显。3.4.1 原生RecyclerView复用原理剖析原生的RecyclerView视图按模板类型进行存储并复用,也就是说模板类型越多,所需存储的模板种类也就越多,导致内存占用越来越大。原理如下图。滑出屏幕的itemType1和itemType2都会在Recycler缓存池保存,等待后面滑进屏幕的条目的复用。3.4.2 细粒度复用优化内存原理剖析在Litho中,item在回收前,会把LithoView中挂载的各个绘制单元拆分出来(解绑),由Litho自己的缓存池去分类回收,在展示前由LithoView按照组件树的样式组装(挂载)各个绘制单元,这样就达到了细粒度复用的目的。原理如下图。滑出屏幕的itemType1会被拆分成一个个的视图单元。LithoView容器由Recycler缓存池回收,其他视图单元由Litho的缓存池分类回收。使用细粒度复用的RecyclerView的缓存池不再需要区分模板类型来缓存大量的视图模板,只需要缓存LithoView容器。细粒度回收的视图单元数量要远远小于原来缓存在各个视图模板中的视图单元数量。4. 实践美团对Litho进行了二次开发,在美团的MTFlexbox动态化实现方案(简称动态布局)中把Litho作为底层UI渲染引擎来使用。通过动态布局的预览工具,为Litho提供实时预览能力,同时可以有效发挥Litho的性能优化效果。目前Litho+动态布局的实现方案已经应用在了美团App中,给美团App带来了不错的性能提升。后续博主会详细介绍Litho+动态布局在美团性能优化的实践方案。4.1 内存数据由于Litho中使用了大量Drawable替换View,并且实现了视图单元的细粒度复用,因此复杂列表滑动时内存优化比较明显。美团首页内存占用随滑动页数变化走势图如下。随着一页一页地滑动,内存优化了30M以上。(数据采集自Vivo x20手机内存占用情况)4.2 FPS数据FPS的提升主要得益于Litho的异步布局能力,提前计算布局可以减少滑动时的帧率波动,所以滑动过程较平稳,不会有高低起伏的卡顿感。(数据采集自魅蓝2手机一段时间内连续fps的波动情况)5. 总结Litho相对于传统Android是颠覆式的,它采用了React的思路,使用声明式的API来编写UI。相比于传统Android,确实在性能优化上有很大的进步,但是如果完全使用Litho开发一款应用,需要自己实现很多组件,而Litho的组件需要在编译时生成,实时预览方面也有所欠缺。相对于直接使用Litho的高成本,把Litho封装成Flexbox布局的底层渲染引擎是个不错的选择。6. 参考资料Litho官网说一说 Facebook 开源的 LithoReact官网Yoga官网7. 作者简介何少宽,美团Android开发工程师,2015年加入美团,负责美团平台终端业务研发工作。张颖,美团Android开发工程师,2017年加入美团,负责美团平台终端业务研发工作。

March 15, 2019 · 2 min · jiezi

UI设计介绍

March 6, 2019 · 0 min · jiezi

10套好用的前端框架、设计组件库推荐

资源 | 10套好用的前端框架、设计组件库推荐原创文章分类:规范/资料版权: 保留作者信息 禁止商业使用 修改作品禁止更改版权信息12962595002018-02-18 63.7。Web、Mobile、和小程序。Image titleNo.1 Ant Design UI 框架官网地址:ant.design体验地址:xcloud.alipay.com设计资源包:DownloadGithub:github.com/ant-designImage title解析:蚂蚁金服出品,基于 ReactJS。一个服务于企业级产品的设计体系,支持桌面端、手机端。基于『确定』和『自然』的设计价值观,通过模块化的解决方案,让设计者专注于更好的用户体验。No.2 Element UI 框架 官网地址:element.eleme.io设计资源包:DownloadGithub:github.com/elemefeImage title解析:饿了么出品,基于 VueJS、React、Angular。一套为开发者、设计师和产品经理准备的桌面端组件库。No.3 Bootstrap 框架官网地址:getbootstrap.com案例展示:expo.getbootstrap.comGithub:github.com/twbs/bootstrapImage title解析:Bootstrape 是经典的前端框架。主要是有很多的主题,可以看下案例展示。No.4 ZUI H5 框架官网地址:zui.sexy移动端框架:zui.sexy/mGithub:github.com/easysoft/zuiImage title解析:一套开源 HTML5 跨屏框架,基于 Bootstrap 深度定制开源前端实践方案,帮助你快速构建现代跨屏应用。No.5 Muse UI 框架官网地址:muse-ui.orgGithub:github.com/museui/muse-uiImage title解析:Muse UI 基于 Vue2.0 开发,基本实现了 Material Design 设计规范类的所有组件,另外还开发许多的功能性的组件。No.6 Foundation UI 框架官网地址:foundation.zurb.com案例展示:zurb.com/responsiveImage title解析:一套灵活适配于任何设备的框架,可以非常便捷地搭建出好看的可适配网站、Apps和邮件。资源一直在更新,并且有很多 Html 的模板能帮助你快速开始。No.7 WePY 小程序框架官网地址:tencent.github.io/wepyGithub:github.com/Tencent/wepyImage title解析:一套让小程序支持组件化开发的框架。WePY 框架在开发过程中参考了 Vue 等现有框架的一些语法风格和功能特性,对原生小程序的开发模式进行了再次封装,熟悉 Vue 的你开发起来可以更加得心应手。No.8 Amaze H5 框架官网地址:amazeui.orgGithub:github.com/amazeui/amazeuiImage title解析:Amaze 以移动优先(Mobile first),从小屏逐步扩展到大屏,最终实现所有屏幕适配。相比国外框架,更关注中文排版效果;兼顾国内主流浏览器及 App 内置浏览器兼容支持。No.9 ZanUI 组件库官网地址:youzanyun.com/zanuiGithub:github.com/youzan/vantImage title解析:有赞出品,支持移动、PC、小程序。可以看看它的移动端 Vue 组件库 Vant。No.10 ICE 组件库官网地址:alibaba.github.io/ice体验地址:alibaba.github.io/ice/#/block下载GUI工具:DownloadGithub:github.com/alibaba/iceImage title解析:阿里巴巴出品,基于 ReactJS。海量可复用物料,可以通过 GUI 工具快速构建中后台应用。但据说文档有点坑!所以只推荐设计师看看组件,程序员暂不推荐使用! ...

March 6, 2019 · 1 min · jiezi

UI2Code智能生成Flutter代码——版面分析篇

开篇: 在《UI2CODE–整体设计》篇中,我们提到UI2CODE工程的第一步是版面分析,如果是白色的简单背景,我们可以像切西瓜一样,将图片信息切割为GUI元素。但是在实际生产过程中,UI的复杂度会高很多。本篇我们将围绕版面分析这块内容分享我们的探索过程。架构设计: 版面分析主要处理UI图像的前景提取和背景分析:通过前后景分离算法,将UI视觉稿剪裁为GUI元素:背景分析:通过机器视觉算法,分析背景颜色,背景渐变方向,以及背景的连通区域。前景分析:通过深度学习算法,对GUI碎片进行整理,合并,识别。背景分析: 背景分析的关键在于找到背景的连通区域和闭合区间; 我们从一个实际的应用场景来分析整个背景提取的过程:我们期望将上一张图片,通过UI2CODE,来提取GUI元素。第一步:判断背景区块,通过sobel,Lapacian,canny等边缘检测的方法计算出梯度变化方向,从而得到纯色背景和渐变色背景区域。基于拉普拉斯算子的背景区域提取离散拉普拉斯算子的模板:扩展模板:当该区域点与模板点乘,得到的数值越小越是平坦的区域,即接近背景区域。如果是渐变色区域,则统计它的运动趋势。(左->右,右->左,上->下, 下->上)提取效果如下:我们发现上图虽然能大致提取背景轮廓,但是对于有渐变色的背景还是比较粗糙。因此我们通过统计背景运动趋势的方式来判定它是否存在渐变色背景。如果存在,我们将通过第二步进行精细化处理。第二步:基于漫水填充算法,选取漫水的种子节点,滤除渐变色背景区域噪声。 def fill_color_diffuse_water_from_img(task_out_dir, image, x, y, thres_up = (10, 10, 10), thres_down = (10, 10, 10), fill_color = (255,255,255)): """ 漫水填充:会改变图像 """ # 获取图片的高和宽 h, w = image.shape[:2] # 创建一个h+2,w+2的遮罩层, # 这里需要注意,OpenCV的默认规定, # 遮罩层的shape必须是h+2,w+2并且必须是单通道8位,具体原因我也不是很清楚。 mask = np.zeros([h + 2, w + 2], np.uint8) # 这里执行漫水填充,参数代表: # copyImg:要填充的图片 # mask:遮罩层 # (x, y):开始填充的位置(开始的种子点) # (255, 255, 255):填充的值,这里填充成白色 # (100,100,100):开始的种子点与整个图像的像素值的最大的负差值 # (50,50,50):开始的种子点与整个图像的像素值的最大的正差值 # cv.FLOODFILL_FIXED_RANGE:处理图像的方法,一般处理彩色图象用这个方法 cv2.floodFill(image, mask, (x, y), fill_color, thres_down, thres_up, cv2.FLOODFILL_FIXED_RANGE) cv2.imwrite(task_out_dir + “/ui/tmp2.png”, image) # mask是非常重要的一个区域,这块区域内会显示哪些区域被填充了颜色 # 对于UI自动化,mask可以设置成shape,大小以1最大的宽和高为准 return image, mask处理过后的效果如下:第三步:通过版面切割,提取GUI元素,切割方法之前有提到过。这个时候我们已经成功提取了80%的GUI元素,但是叠加图层部分元素还无法解析。第四步:通过霍夫直线、霍夫圆和轮廓查找的方式找到对称的轮廓区域。对提取的轮廓区域做第二步到第四步的递归处理。复杂背景的提取是后续前景分析的基础。当提炼出背景区域后,我们就可以通过连通域分析前景区域,并利用组件识别的方式分析前景类别,再通过语义分析等方式对前景做拆分和合并。背景分析小结:对比较简单的渐变色背景,通过上述四步基本都能够解决。基于霍夫直线和霍夫圆的思想,去查找特定的轮廓区域,并分析这些轮廓区域做细化分析。再通过漫水填充的方式消除渐变色背景。对于复杂背景的处理,可以基于目标检测的方法,找到一些特定含义的内容。再利用马尔科夫随机场(mrf)细化边缘特征。前景分析前景分析的关键在于组件完整性切割与识别;我们通过连通域分析,防止组件碎片化,再通过机器学习识别组件类型,再通过组件类型进行碎片化合并,反复执行上述动作,直到无特征属性碎片。但是有些情况会比较复杂,我们通过瀑布流中提取一个完整item为例:概述闲鱼页面中瀑布流卡片识别是实现布局分析的一个重要步骤,需求是当卡片完整出现在截屏图像中时(允许图标遮挡)需要识别出来,卡片被背景部分遮挡时不应该识别出来。下图所示的瀑布流卡片样式,由于其布局紧凑且样式繁多,导致容易产生漏检或误检。瀑布流卡片样式 a)红框显示卡片部分被图标遮挡 b)红框显示卡片内图片颜色和卡片外背景颜色接近基于边缘梯度或连通域的传统图像处理方法能根据图像本身的灰度或者形状特征提取出瀑布流卡片的轮廓,优点是定位精度高、运算速度快。缺点是容易受到干扰,召回率不高。基于目标检测或者特征点检测的深度学习方法采用有监督的方式学习卡片的样式特征,优点是不易受到干扰,召回率很高。缺点是因为涉及回归过程,定位精度比传统图像处理方法要低,并且需要大量的人工标注,运算速度也比传统图像处理方法要慢。受集成学习的启发,通过融合传统图像处理方法和深度学习方法,结合两者各自的优点,最终能得到具有较高精确率、召回率和定位精度的识别结果。传统图像处理1.算法流程整个算法流程如下图所示: 1》. 输入的瀑布流卡片图像转换成灰度图后使用对比度受限的自适应直方图均衡化(CLAHE)进行增强 2》. 使用Canny算子进行边缘检测得到二值化图像 3》. 对二值化图像进行形态学膨胀处理,连接断开的边缘 4》. 提取连续边缘的外层轮廓,并基于轮廓包含区域的面积大小丢弃面积较小的轮廓,输出候选轮廓 5》. 使用Douglas-Peucker算法进行矩形逼近,保留最像矩形的外轮廓,输出新的候选轮廓 6》. 最后对第4步的候选轮廓进行水平和垂直方向投影得到平滑的轮廓作为输出2.边缘检测算法流程中1》-3》可以归为边缘检测部分。 受各种因素影响,图像会出现降质,需要对其进行增强来提高边缘检测的效果。使用全图单一的直方图进行均衡化显然不是最好的选择,因为截取的瀑布流图像不同区域对比度可能差别很大,增强后的图像可能会产生伪影。在单一直方图均衡化的基础上,学者基于分块处理的思想提出了自适应的直方图均衡化算法(AHE),但是AHE在增强边缘的同时有时候也会将噪声放大。后来学者在AHE的基础上提出了CLAHE,利用一个对比度阈值来去除噪声的干扰,如下图所示直方图,CLAHE不是将直方图中超过阈值的部分丢弃,而是将其均匀分布于其他bin中。Canny算子是一种经典的边缘检测算子,它能得到精确的边缘位置。Canny检测的一般步骤为:1)用高斯滤波进行降噪 2)用一阶偏导的有限差分计算梯度的幅值和方向 3)对梯度幅值进行非极大值抑制 4)用双阈值检测和连接边缘。实验过程中,需要多次尝试选择较好的双阈值参数。 检测出来的边缘在某些局部地方会断开,可以采用特定形状和尺寸的结构元素对二值化图像进行形态学膨胀处理来连接断开的边缘。边缘检测的结果如下图所示,其中c)中CLAHE设定对比度阈值为10.0,区域大小为(10,10),d)中Canny检测设置双阈值为(20,80),e)中形态学膨胀结构元素使用大小为(3,3)的十字线。3.轮廓提取算法流程中4》-6》可以归为轮廓提取部分。 二值图像形态学膨胀处理后,首先提取连续边缘的外层轮廓。如下图所示,对于只有0和1的二值图像,假设S1为像素值为0的一堆背景点,S2为像素值为1的一堆前景点,外层轮廓B1为一堆前景点最外围的点,内层轮廓B2为一堆前景点最内部的点。通过对二值图像进行行扫描给不同轮廓边界赋予不同的整数值,从而确定轮廓的类型以及之间的层次关系。提取出外层轮廓后通过计算轮廓包含的面积大小丢弃面积较小的外层轮廓,输出第一步候选轮廓。闲鱼页面瀑布流卡片轮廓近似于矩形,在四个角由弧形曲线连接。对于提取的候选轮廓使用Douglas-Peucker算法进行矩形逼近,保留形状接近矩形的外轮廓。Douglas-Peucker算法通过将一组点表示的曲线或多边形拟合成另一组更少的点,使得两组点之间的距离满足特定的精度。之后输出第二步候选轮廓。 通过对第二步候选轮廓所处位置对应的第一步候选轮廓进行水平和垂直方向投影,去除毛刺影响,输出矩形轮廓。轮廓提取的结果如下图所示,其中c)中轮廓包含面积设置的阈值为10000,d)中Douglas-Peucker算法设置的精度为0.01轮廓长度。本文所有提取的轮廓均包含输入框。我们再介绍下机器学习如何处理:深度学习算法1.目标检测算法传统算法中提出采用传统图像处理方法提取轮廓特征,这样会带来一个问题:当图像不清晰或者有遮挡的情况下无法提取出轮廓,即召回率不是很高。基于卷积神经网络的目标检测算法能通过输入大量样本数据,学习到更具有代表性和区别性的特征。目前目标检测算法主要分成两个派系:以R-CNN家族为代表的两阶段流和以YOLO、SSD为代表的一阶段流。一阶段流直接对预测的目标进行分类和回归,优点是速度快,缺点是mAP整体上没有两阶段流高。两阶段流在对预测的目标进行分类和回归前需要先生成候选的目标区域,这样训练时更容易收敛,因此优点是mAP高,缺点是速度上不如一阶段流。不管是一阶段流还是两阶段流,通用的目标检测推理过程如图所示。输入一张图像到特征提取网络(可选择VGG、Inception、Resnet等成熟的CNN网络)得到图像特征,然后将特定区域特征分别送入分类器和回归器进行类别分类和位置回归,最后将框的类别和位置进行输出。2.Faster R-CNNFaster R-CNN对R-CNN家族最大的贡献是将生成候选目标区域的过程整合到整个网络中,使得综合性能有了较大提高,尤其是在检测速度上。Faster R-CNN的基本结构如图所示。主要分为4个部分:1)conv layers。作为一种特征提取网络,使用一组基础的conv+relu+pooling层提取图像的特征,该特征被共享用于后续RPN网络和全连接层。2)Region Proposal Network。该网络用于生成候选目标框,通过softmax判断候选框是属于前景还是背景,并且通过候选框回归修正候选框位置。3)RoI pooling。收集输入的特征图和候选区域,将这些候选区域映射到固定大小并送入后续全连接层。4)classifer。计算候选框的具体类别,并且再次回归修正候选框的位置。使用Faster R-CNN进行瀑布流卡片的识别,得到下图的结果。传统算法与机器学习的融合1.融合流程描述的传统图像方法能够获得高定位精度的卡片位置,但受卡片本身模式的影响,召回率不高)中右边卡片没有检测到。上文描述的基于目标检测的深度学习方法具有较高的泛化能力,能获得较高的召回率,但是回归过程无法得到高定位精度的卡片位置)中卡片都能检测出来,但有两个卡片的边缘几乎粘连在了一起。 将两种方法得到的结果融合在一起,能同时获得高精确率、高召回率、高定位精度的检测结果。融合过程如下:输入一张图像,并行运行传统图像处理方法和深度学习方法,分别得到提取的卡片框trbox和dlbox。定位精度以trbox为标杆,精确率和召回率以dlbox为标杆筛选trbox。规则为当trbox与dlbox的IOU大于某个阈值时(比如0.8)保留此trbox,否则丢弃,得到trbox1筛选dlbox。规则为当dlbox与trbox1的IOU大于某个阈值时(比如0.8)丢弃此dlbox,否则保留,得到dlbox1修正dlbox1位置。规则为dlbox1的每条边移动到距离其最近的一条直线上,约束条件为移动的距离不能超过给定的阈值(比如20个像素),并且移动的边不能跨越trbox1的边,得到修正的dlbox2输出trbox1+dlbox2为最终融合的卡片框2.结果先介绍几个基本指标: True Positive(TP):被模型预测为正的正例数 True Negative(TN):被模型预测为负的负例数 False Positive(FP):被模型预测为正的负例数 False Negative(FN):被模型预测为负的正例数 精确率(Precision) = TP/(TP+FP):反映了被模型预测的正例中真正的正样本所占比重 召回率(Recall) = TP/(TP+FN):反映了被模型正确预测的正例占总的正样本比重 定位精度(IOU) = 两个框的交集大小/两个框的并集大小上图分别显示了不同方法识别的卡片,d)相对于b)的优势是提高了召回率,d)相对于c)的优势是提高了定位精度。图一图二图三显示了一些其他实例图像的识别,每行图像是一类实例图,第一列是原始图像,第二列是传统图像处理识别的卡片,第三列是深度学习识别的卡片,第四列是融合的卡片。图一图二能够准确识别卡片轮廓:图三融合卡片的下边缘并没有完全贴合,这是因为融合步骤中修正dlbox1位置时,采用传统图像处理方法寻找临域范围内最近的直线,受到图像样式的影响,找到的直线并不是期望的卡片下边缘。实验过程中随机截取了50张闲鱼瀑布流卡片图像,共有卡片96个(不包含输入框),对每张图像分别采用传统图像处理方法、深度学习方法、融合方法得到卡片的识别结果,其中传统图像处理方法共识别出65个卡片,深度学习方法共识别出97个,融合后共识别出98个。精确率、召回率、定位精度如下表所示。融合后识别结果结合了传统图像处理方法定位精度高、深度学习方法召回率高的优点。不同方法结果前景算法小结通过对闲鱼页面瀑布流卡片识别过程中的描述,我们简单介绍了前景处理的探索,通过机器视觉算法和机器学习算法协同完成前景元素的提取和识别。结束语本篇我们通过对前景提取和背景分析的介绍,提出了一种通过传统图像处理和深度学习相融合的方法,来得到高精确率、高召回率和高定位精度的识别结果。但方法本身还存在一些瑕疵,比如融合过程对组件元素进行修正时也会受到图像样式的干扰,后续这部分可以进一步进行优化。本文作者:闲鱼技术-上叶,仝辉,深宇阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 1, 2019 · 1 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

SAP 前端技术的演化史简介

Jerry之前曾经写过一篇微信公众号文章,题目叫<<SAP UI和Salesforce UI开发漫谈>>关注我的公号“汪子熙”后,在历史菜单“前端开发相关”里即可找到这篇文章:该文章简单回顾了SAP UI技术的发展历史,然后提了下Salesforce的Apex和Lighting Component等技术和框架。目录SAP UISAP GUI + DynproWeb DynproBSP/CRM WebClient UISAP UI5/FioriUI5 in SAP Cloud for CustomerHybris Enterprise Commerce PlatformSalesforce UIApexLightning ExperienceAura FrameworkLightning Component FrameworkVisualforce我也画了张简单的图:R1和针对于大型机的R2对我们来说实在太古老了,对我们来说,只能通过SAPGUI里的复古主题,即Classical Theme来体验一下这些老古董的外观风采。到了1992年出现了类似JSP技术的BSP(business server page),能够借助在服务器端执行的ABAP语言实现动态网页效果。在运行时,每个BSP页面会自动生成一个临时的ABAP类,执行这些BSP页面上嵌入的ABAP代码,执行的结果再渲染成原生的HTML代码。值得一提的是,BSP技术兼容普通的HTML/JavaScript应用,换句话说,几乎所有能运行在除Netweaver以为的web服务器上的基于HTML/JavaScript的web应用,也能以BSP为载体,运行在Netweaver上。因此,即使是如今SAP的旗舰级产品S/4HANA里的很多Fiori UI应用,也是以BSP应用为载体存储在Netweaver上的。比如S/4HANA物料主数据管理的Fiori应用,其名称在Chrome开发者工具里能看到:这个BSP应用在Netweaver上能找到:诞生于1992年的BSP技术到了今天还在服役,这本身就是一个奇迹了。当然它本身由于历史原因也有一些局限:开发效率不够高,没有类似后来UI5里控件库的概念,导致开发人员需要重复造很多轮子。SAP后来自己也发布了一些BSP Extension,类似JSP里的tag,以此来弥补开发效率的缺陷。另外BSP的开发工具在SAPGUI里只有事务码SE80,这个工具在做HTML和JavaScript开发时显得不够友好。因此后期SAP Fiori开发也采取了在本地现代IDE比如Eclipse里做开发,完毕后再上传到Netweaver自动生成BSP的方式。没有MVC的概念,在大型企业级应用开发中显得力不从心。正是由于暴露了这两个缺陷,促成了WebUI和Webdynpro的问世。对这两种前端技术的详细介绍,请参考Jerry之前提到的微信文章:SAP UI和Salesforce UI开发漫谈,这里不再重复,只是聊聊一些该文章中没有提过的内容。ABAP Webdynpro的亮点就是能够以所见即所得的方式进行UI界面开发,缺点是不再支持类似BSP那样兼容传统的HTML/JavaScript,因此无法实现某些对界面复杂度和交互性要求较高的需求。而WebUI在继承了BSP所有优点的同时,在BSP基础上提供了对MVC的封装,使得开发效率大大提高,同时开发出来的Web应用结构清晰,不再会出现一个视图页面几千行代码的情况。下图是一个典型的WebUI模型,MVC三层在workbench里有清晰的界定。WebUI和ABAP Webdynpro至今仍广泛应用于SAP产品中。在S/4HANA的CRM模块里,WebUI继续扮演着非常重要的角色,详情请阅读我下面这篇文章:Hello World, S/4HANA for Customer Management 1.0而Webdynpro,是SAP SRM UI开发的主流技术。搜索公网上所有使用了SAP BSP技术的网站:https://www.google.com/search…:/sap/bc/bsp/&gws_rd=ssl使用了Webdynpro的:随着时间的推移,用户对移动设备上访问网页的体验要求越来越高,因此有了SAP从业者现在很熟悉的前端技术:SAP UI5。关于UI5最新的技术发展方向,请关注我的公众号“汪子熙”,阅读我写的这篇文章:Fiori Fundamentals和SAP UI5 Web Components要获取更多Jerry的原创文章,请关注公众号"汪子熙":

February 24, 2019 · 1 min · jiezi

开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题

导语发布app后,开发者最头疼的问题就是如何解决交付后的用户侧问题的还原和定位,是业界缺乏一整套系统的解决方案的空白领域,闲鱼技术团队结合自己业务痛点在flutter上提出一套全新的技术思路解决这个问题。我们透过系统底层来捕获ui事件流和业务数据的流动,并利用捕获到的这些数据通过事件回放机制来复现线上的问题。本文先介绍flutter触摸手势事件原理,接着介绍里面怎样录制flutter ui手势事件,然后介绍怎样还原回放flutter ui手势事件,最后附上包括native录制回放的整体框架图。为了便于理解本文,读者可以先阅读我之前写的关于native录制和回放文章《千人千面线上问题回放技术》背景现在的app基本都会提供用户反馈问题的入口,然而提供给用户反馈问题一般有两种方式:直接用文字输入表达,或者截图直接录制视频反馈这两种反馈方式常常带来以下抱怨:用户:输入文字好费时费力开发1:看不懂用户反馈说的是什么意思?开发2:大概看懂用户说的是什么意思了,但是我线下没办法复现哈开发3:看了用户录制的视频,但是我线下没办法重现,也定位不到问题所以:为了解决以上问题,我们用一套全新的思路来设计线上问题回放体系Flutter 手势基础知识如果要录制和回放flutter ui事件,那么我们首先必须了解flutter ui手势基本原理。1. Flutter UI触摸原始数据Pointer我们可以把Flutter中的手势系统分两层概念来理解。第一层概念为原始触摸数据(pointer),它描述了屏幕上指针(例如,触摸,鼠标和触控笔)的时间,类型,位置和移动。 第二层概念为手势,描述由一个或多个原始移动数据组成的语义动作。一般情况下单独的原始触摸数据没有任何意义。原始触摸数据是由系统传给native,native再通过flutter view channel传给flutter。flutter接收native传来的原始数据接口如下: void _handlePointerDataPacket(ui.PointerDataPacket packet) { // We convert pointer data to logical pixels so that e.g. the touch slop can be // defined in a device-independent manner. _pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio)); if (!locked) _flushPointerEventQueue(); }2. Flutter UI碰撞测试当屏幕接收到触摸时,dart Framework会对您的应用程序执行碰撞测试,以确定触摸与屏幕相接的位置存在哪些视图(renderobject)。 触摸事件然后被分发到最内部的renderobject上。 从最内部renderobject开始,这些事件在renderobject树中向上冒泡传递,通过冒泡传递最后把所有的renderobject遍历出来,从这个传递机制可想而知,遍历出来renderobject列表里的最后一个是WidgetsFlutterBinding(严格来讲WidgetsFlutterBinding不是renderobject),后面会介绍到WidgetsFlutterBinding。 void _handlePointerEvent(PointerEvent event) { assert(!locked); HitTestResult result; if (event is PointerDownEvent) { assert(!_hitTests.containsKey(event.pointer)); result = HitTestResult(); hitTest(result, event.position); _hitTests[event.pointer] = result; assert(() { if (debugPrintHitTestResults) debugPrint(’$event: $result’); return true; }()); } else if (event is PointerUpEvent || event is PointerCancelEvent) { result = _hitTests.remove(event.pointer); } else if (event.down) { result = _hitTests[event.pointer]; } else { return; // We currently ignore add, remove, and hover move events. } if (result != null) dispatchEvent(event, result); }上面代码以 histTest()检测当前触摸 pointer event 涉及到哪些视图。最后通过dispatchEvent(event, result)来处理该事件。void dispatchEvent(PointerEvent event, HitTestResult result) { assert(!locked); assert(result != null); for (HitTestEntry entry in result.path) { try { entry.target.handleEvent(event, entry); } catch (exception, stack) { } } }上面的代码就是用来分别调用每个视图(RenderObject)的手势识别器独自处理当前触摸事件(决定是否接收此事件)。entry.target是每个widget对应的RenderObject,所有的RenderObject都需要实现(implements)HitTestTarget类的接口,HitTestTarget里面有就有handleEvent这个接口,所以每个RenderObject都需要实现handleEvent这个接口, 这个接口就是用来处理手势识别。abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget除了最后一个WidgetsFlutterBinding外,其他视图RenderObject调用自己的handleEvent来识别手势,其作用就是判断当前手势是否要放弃,如果不放弃则丢到一个路由器里(这个路由器就是手势竞技场)最后由WidgetsFlutterBinding 调用handleEvent统一决议这些手势识别器最终谁胜出,所以这里WidgetsFlutterBinding.handleEvent其实就是统一处理接口,它的代码如下: void handleEvent(PointerEvent event, HitTestEntry entry) { pointerRouter.route(event); if (event is PointerDownEvent) { gestureArena.close(event.pointer); } else if (event is PointerUpEvent) { gestureArena.sweep(event.pointer); } }3. Flutter UI手势决议从上面的介绍可以得出一次触摸事件可能触发多个手势识别器。框架通过让每个识别器加入一个“手势竞争场”来决议用户想要的手势。“手势竞争场”使用以下规则来决议哪个手势胜出,非常简单在任何时候,任何识别器都可以自己宣布失败并主动离开“手势竞争场”。如果在当前“竞争场”中只剩下一个识别器,那么剩下来的就是赢家,赢家意味着独自接收此触摸事件并做出响应动作在任何时候,任何识别器都可以自己宣布胜利,并且最终就是它胜利,所有剩下的其他识别器都会失败4. Flutter UI手势例子下面示例表示屏幕window由ABCDEFKG视图组成,其中A视图是根视图,即是最底下的视图。红圈表示触摸点位置,触摸落在G视图的中间位置。根据碰撞测试,遍历出响应此触摸事件的视图路径:WidgetsFlutterBinding <— A <— C <— K <— G (其中GKCA是renderObject)遍历路径列表后,开始调用各自的视图(GKCA)entry.target.handleEvent来把自己识别器放到竞技场里参加决议,当然有些视图由于根据自己的逻辑判断主动放弃识别该触摸事件。这个处理过程如下图按G->K->C->A->WidgetsFlutterBinding顺序分别调用handleEvent()方法,最后通过WidgetsFlutterBinding调用自己的handleEvent()接口来统一决议最终哪个手势识别器胜出。胜出的那个手势识别器通过回调方法回调到上层业务代码,流程如下Flutter UI录制从上面的flutter手势处理可知,我们只需要在手势识别器回调上包装回调方法,即可拦截到手势回调方法,这样我们就可以在拦截过程读到WidgetsFlutterBinding <— A <— C <— K <— G链路的这棵视图树。我们只需要把这个棵树,树上的节点相关属性和手势类型记录下来,那回放时,通过这些信息去匹配到当前界面上的对应视图即可回放。下面是tap事件的录制代码,其他类型手势的录制代码原理一样,这里略过。 static GestureTapCallback onTapWithRecord(GestureTapCallback orgOnTap, BuildContext context) { if (null != orgOnTap && null != context) { final GestureTapCallback onTapWithRecord = () { if(bStartRecord) { saveTapInfo(context, TouchEventUIType.OnTap,null); } if (null != orgOnTap) { orgOnTap(); } }; return onTapWithRecord; } return orgOnTap; }static void saveTapInfo(BuildContext context, TouchEventUIType type, Offset point) { if(null == point && null != pointerPacketList && pointerPacketList.isNotEmpty) { final ui.PointerDataPacket last = pointerPacketList.last; if(null != last && null != last.data && last.data.isNotEmpty) { final ui.Rect rect = QueReplayTool.getWindowRect(context); point = new Offset(last.data.last.physicalX / ui.window.devicePixelRatio - rect.left, last.data.last.physicalY /ui.window.devicePixelRatio - rect.top); } } final RecordInfo record = createTapRecordInfo(context, type, point); if(null != record) { FlutterQuestionReplayPlugin.saveRecordDataToNative(record); } clearPointerPacketList(); }录制流程图如下:Flutter UI回放ui回放分两部分,第一部分通过录制的相关信息match到当前界面相应视图,第二部分是在此视图上进行模拟相关手势动作,这部分是个难点,也是重点,其中涉及到怎样生成原始的触摸数据信息,里面有时间,类型,坐标,方向,如果这些信息设置不合理或者错误会导致crash,还有滚动距离不符需要补偿,怎么补偿等等。下面是滚动事件回放流程图,其他类型手势的回放原理一样。上面的预处理,识别消耗指的是在滚动开始时,手势识别器要判断是否符合滚动手势所需要滚动的距离。所以我们为了让其控件滚动首先要生成一些触摸点数据,让手势识别器识别为滚动事件。这样才能进行后续的滚动动作。下面是滚动处理逻辑代码,如下: void verticalScroll(double dstPoint, double moveDis) { preReplayPacket = null; if (0.0 != moveDis) { //此处计算滚动方向,和滚动单元像素偏移,由于代码太长略过 int count = ((ui.window.devicePixelRatio * moveDis) / (unit.abs())).round() * 2; if (count < minCount) { count = minCount; //保证最少偏移50/2=25 小于这个数 可能没反应,因为被其他控件检测滚动消耗掉了 //还有就是如果count太小,count被scroll view消耗完前并没有滚动,这是就触摸结束了(ui.PointerChange.up),那可能引起cell //点击事件跳转事件 } final double physicalX = rect.center.dx * ui.window.devicePixelRatio; //376.0; double physicalY; final double needOffset = (count * unit).abs(); final double targetHeight = rect.size.height * ui.window.devicePixelRatio; final int scrollPadding = rect.height ~/ 4; if (needOffset <= targetHeight / 2) { physicalY = rect.center.dy * ui.window.devicePixelRatio; } else if (needOffset > targetHeight / 2 && needOffset < targetHeight) { physicalY = (orgMoveDis > 0) ? (rect.bottom - scrollPadding) * ui.window.devicePixelRatio : (rect.top + scrollPadding) * ui.window.devicePixelRatio; } else { physicalY = (orgMoveDis > 0) ? (rect.bottom - scrollPadding) * ui.window.devicePixelRatio : (rect.top + scrollPadding) * ui.window.devicePixelRatio; count = ((rect.height - 2 * scrollPadding) * ui.window.devicePixelRatio / unit.abs()) .round(); } final List<ui.PointerDataPacket> packetList =createTouchDataList(count, unit, physicalY, physicalX); exeScroolTouch(packetList,dstPoint); } else { new Timer(const Duration(microseconds: fpsInterval), () { replayScrollEvent(); }); } }上面代码大概处理逻辑:1.计算滚动方向,每个生成的触摸数据偏移单元 2.计算滚动的开始位置 3.生成滚动原始触摸数据列表 4.循环发射原始触摸数据,并计算是否滚动到指定的位置,如果还达不到指定的位置,则继续补给生成滚动原始触摸数据列表代码如下:第一数据是down触摸数据,其他都是move触摸数据。up数据在这里不需要生成,当滚动距离到目标位置后才另外生成up触摸数据。为什么这样设计?此处留给大家思考!List<ui.PointerDataPacket> createTouchDataList(int count,double unit,double physicalY,double physicalX) { final List<ui.PointerDataPacket> packetList = <ui.PointerDataPacket>[]; int uptime = 0; for (int i = 0; i < count; i++) { ui.PointerChange change; if (0 == i) { change = ui.PointerChange.down; } else { change = ui.PointerChange.move; physicalY += unit; if (i < 15) //前面几个点让在短时间内偏移的距离长点 这样避开单击和长按事件 { physicalY += unit; physicalY += unit; } } uptime += replayOnePointDuration; final ui.PointerData pointer = new ui.PointerData( timeStamp: new Duration(microseconds: uptime), change: change, kind: ui.PointerDeviceKind.touch, device: 1, physicalX: physicalX, physicalY: physicalY, buttons: 0, pressure: 0.0, pressureMin: 0.0, pressureMax: touchPressureMax, distance: 0.0, distanceMax: 0.0, radiusMajor: downRadiusMajor, radiusMinor: 0.0, radiusMin: downRadiusMin, radiusMax: downRadiusMax, orientation: orientation, tilt: 0.0); final List<ui.PointerData> pointerList = <ui.PointerData>[]; pointerList.add(pointer); final ui.PointerDataPacket packet = new ui.PointerDataPacket(data: pointerList); packetList.add(packet); } return packetList; }循环发射原始触摸数据,并判断是否继续补给代码如下:我们以定时器不断的往系统发送触摸数据,每次发送数据前都需要判断是否已经达到目标位置。void exeScroolTouch(List<ui.PointerDataPacket> packetList,double dstPoint){ Timer.periodic(const Duration(microseconds: fpsInterval), (Timer timer) { final ScrollableState state = element.state; final double curPoint = state.position.pixels;//ui.window.physicalSize.height*state.position.pixels/RecordInfo.recordedWindowH; final double offset = (dstPoint - curPoint).abs(); final bool existOffset = offset > 1 ? true : false; if (packetList.isNotEmpty && existOffset) { sendTouchData(packetList, offset); } else if (packetList.isNotEmpty) { record.succ = true; timer.cancel(); packetList.clear(); if (null != preReplayPacket) { final ui.PointerDataPacket packet = createUpTouchPointPacket(); if (null != packet) { ui.window.onPointerDataPacket(packet); } } new Timer(const Duration(microseconds: fpsInterval), () { replayScrollEvent(); }); } else if (existOffset) { record.succ = true; timer.cancel(); packetList.clear(); final ui.PointerDataPacket packet = createUpTouchPointPacket(); if (null != packet) { ui.window.onPointerDataPacket(packet); } verticalScroll(dstPoint, dstPoint - curPoint); } else { finishReplay(); } }); }问题回放整体框架图下图包括native和flutter,包括ui和数据。总结本文大概介绍了flutter ui手势问题回放,核心部分由四部分组成,一是flutter手势原理,二是flutter ui录制,三是flutter ui回放,四是整个框架图,由于篇幅有限,这四分部都介绍比较笼统,不够详细,请谅解!flutter录制回放代码其实很多,我这里只是附上比较重要,而且易于理解的代码。其他不重要或不易读懂的代码都省掉了。如果对里面的技术点感兴趣,你可以关注我们的公众号。我们后续会单独对里面的技术点详细深入的分析发文。如果觉得上面有错误的地方,请指出。谢谢后续的深入到目前为止,我们现在的flutter ui录制回放已经开发完成,但我们后续还需要继续优化和深入。我们后续从两个点来深入优化:1.如何在回放时模拟的触摸事件更逼真,比如滚动加速度,一次的滚动其实是一个曲线变化的过程 2.解决手势录制和回放不一致性。举个例子,在键盘里输入123,我们录制时截获到了手势123,但是由于业务上层的bug导致了当时输入3没有响应,输入框里只显示12,我们回放时模拟手势123,最终回放完后输入框显示123,所以这样导致录制和回放不一致性,这个问题怎么解决?这是个麻烦的问题,我们后续会解决。而且已经有这解决方案。本文作者:闲鱼技术–镜空阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 20, 2019 · 4 min · jiezi

把 React 作为 UI 运行时来使用

翻译:疯狂的技术宅原文:https://overreacted.io/react-…本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章很多教程都把 React 作为一个 UI 库来引入。这是很有道理的,因为 React 本身就是一个 UI 库。就像官网上所说的那样。我曾经写过关于构建用户界面中遇到的挑战的文章。但是本文将会用另外一种方式来讲述 React —— 因为它更像是一种编程运行时。本文不会教你任何有关如何创建界面的技巧。 但是它可能会帮你更加深入地理解 React 编程模型。⚠️ 注意:如果你还在学习 React ,请移步到官方文档进行学习本文将会非常深入 —— 所以不适合初学者阅读。 在本文中,我会从最佳原则的角度尽可能地阐述 React 编程模型。我不会解释如何使用它 —— 而是讲解它的工作原理。本文面向有经验的程序员,还有使用过其他 UI 库,但在项目中权衡利弊之后最终选择了 React 的人,我希望它会对你有所帮助!一些人用了很多年 React 却从没有考虑过接下来我要讲述的主题。 这绝对是以程序员而不是以设计者的角度来看待 React。但我认为站在两个不同的角度来重新认识 React 并没有什么坏处。废话少说,让我们开始深入理解 React 吧!宿主树一些程序输出数字。另一些程序输出诗词。不同的语言和它们的运行时通常会对特定的一组用例进行优化, React 也不例外。React 程序通常会输出一个会随时间变化的树。 它有可能是 DOM 树 、iOS 视图层、PDF 原语 ,或者是 JSON 对象 。不过通常我们希望用它来展示 UI 。我们称它为“宿主树”,因为它往往是 React 之外宿主环境中的一部分 —— 就像 DOM 或 iOS 。宿主树通常有它自己的命令式 API 。而 React 就是它上面的那一层。所以 React 到底有什么用呢?非常抽象,它可以帮助你编写可预测的,并且能够操控复杂的宿主树进而响应像用户交互、网络响应、定时器等外部事件的应用程序。当一个专业的工具可以施加特定的约束,并且能从中获益时,它就比一般的工具要好。React 就是这样的典范,并且它坚持两个原则:稳定性。 宿主树是相对稳定的,大多数情况的更新并不会从根本上改变其整体结构。如果应用程序每秒都会将其所有可交互的元素重新排列为完全不同的组合,那将会变得难以使用。那个按钮去哪了?为什么我的屏幕在跳舞?通用性。 宿主树可以被拆分为外观和行为一致的 UI 模式(例如按钮、列表和头像)而不是随机的形状。这些原则恰好适用于大多数 UI 。 不过当输出没有稳定的“模式”时 React 并不适用。例如,React 也许可以帮你写一个 Twitter 客户端,但对于一个 3D 管道屏幕保护程序并没有太大用处。宿主实例宿主树由节点组成,我们称之为“宿主实例”。在 DOM 环境中,宿主实例就是我们通常所说的 DOM 节点 —— 就像当你调用 document.createElement(‘div’) 时获得的对象。在 iOS 中,宿主实例可以是从 JavaScript 到原生视图唯一标识的值。宿主实例有它们自己的属性(例如 domNode.className 或者 view.tintColor )。它们也有可能将其他的宿主实例作为子项。(这和 React 没有任何联系 — 因为我在讲述宿主环境。)通常会有原生 API 用于操控这些宿主实例。例如,在 DOM 环境中会提供像 appendChild、 removeChild、 setAttribute 等一系列的 API 。在 React 应用中,通常你不会调用这些 API ,因为那是 React 的工作。渲染器渲染器告诉 React 如何与特定的宿主环境通信,以及如何管理它的宿主实例。React DOM、React Native 甚至 Ink 都可以被称作 React 渲染器。你也可以创建自己的 React 渲染器 。React 渲染器能以下面两种模式之一进行工作。绝大多数渲染器都被用作“突变”模式。这种模式正是 DOM 的工作方式:我们可以创建一个节点,设置它的属性,在之后往里面增加或者删除子节点。宿主实例是完全可变的。但 React 也能以”不变“模式工作。这种模式适用于那些并不提供像 appendChild 的 API 而是克隆双亲树并始终替换掉顶级子树的宿主环境。在宿主树级别上的不可变性使得多线程变得更加容易。React Fabric 就利用了这一模式。作为 React 的使用者,你永远不需要考虑这些模式。我只想强调 React 不仅仅只是从一种模式转换到另一种模式的适配器。它的用处在于以一种更好的方式操控宿主实例而不用在意那些低级视图 API 范例。React 元素在宿主环境中,一个宿主实例(例如 DOM 节点)是最小的构建单元。而在 React 中,最小的构建单元是 React 元素。React 元素是一个普通的 JavaScript 对象。它用来描述一个宿主实例。// JSX 是用来描述这些对象的语法糖。// <button className=“blue” />{ type: ‘button’, props: { className: ‘blue’ }}React 元素是轻量级的,因为没有任何宿主实例与它绑定在一起。同样,它只是对你想要在屏幕上看到的内容的描述。就像宿主实例一样,React 元素也能形成一棵树:// JSX 是用来描述这些对象的语法糖。// <dialog>// <button className=“blue” />// <button className=“red” />// </dialog>{ type: ‘dialog’, props: { children: [{ type: ‘button’, props: { className: ‘blue’ } }, { type: ‘button’, props: { className: ‘red’ } }] }}(注意:我省略了一些对此解释不重要的属性)但是请记住 React 元素并不是永远存在的 。它们总是在重建和删除之间不断循环。React 元素具有不可变性。例如你不能改变 React 元素中的子元素或者属性。如果你想要在稍后渲染一些不同的东西,需要从头创建新的 React 元素树来描述它。我喜欢将 React 元素比作电影中放映的每一帧。它们捕捉 UI 在特定的时间点的样子。它们永远不会再改变。入口每一个 React 渲染器都有一个“入口”。正是那个特定的 API 让我们告诉 React ,将特定的 React 元素树渲染到真正的宿主实例中去。例如,React DOM 的入口就是 ReactDOM.render :ReactDOM.render( // { type: ‘button’, props: { className: ‘blue’ } } <button className=“blue” />, document.getElementById(‘container’));当我们调用 ReactDOM.render(reactElement, domContainer) 时,我们的意思是:“亲爱的 React ,将我的 reactElement 映射到 domContaienr 的宿主树上去吧。“React 会查看 reactElement.type (在我们的例子中是 button )然后告诉 React DOM 渲染器创建对应的宿主实例并设置正确的属性:// 在 ReactDOM 渲染器内部(简化版)function createHostInstance(reactElement) { let domNode = document.createElement(reactElement.type); domNode.className = reactElement.props.className; return domNode;}在我们的例子中,React 会这样做:let domNode = document.createElement(‘button’);domNode.className = ‘blue’;domContainer.appendChild(domNode);如果 React 元素在 reactElement.props.children 中含有子元素,React 会在第一次渲染中递归地为它们创建宿主实例。协调如果我们用同一个 container 调用 ReactDOM.render() 两次会发生什么呢?ReactDOM.render( <button className=“blue” />, document.getElementById(‘container’));// … 之后 …// 应该替换掉 button 宿主实例吗?// 还是在已有的 button 上更新属性?ReactDOM.render( <button className=“red” />, document.getElementById(‘container’));同样,React 的工作是将 React 元素树映射到宿主树上去。确定该对宿主实例做什么来响应新的信息有时候叫做协调 。有两种方法可以解决它。简化版的 React 会丢弃已经存在的树然后从头开始创建它:let domContainer = document.getElementById(‘container’);// 清除掉原来的树domContainer.innerHTML = ‘’;// 创建新的宿主实例树let domNode = document.createElement(‘button’);domNode.className = ‘red’;domContainer.appendChild(domNode);但是在 DOM 环境下,这样的做法效率很低,而且会丢失 focus、selection、scroll 等许多状态。相反,我们希望 React 这样做:let domNode = domContainer.firstChild;// 更新已有的宿主实例domNode.className = ‘red’;换句话说,React 需要决定何时更新一个已有的宿主实例来匹配新的 React 元素,何时该重新创建新的宿主实例。这就引出了一个识别问题。React 元素可能每次都不相同,到底什么时候才该从概念上引用同一个宿主实例呢?在我们的例子中,它很简单。我们之前渲染了 <button> 作为第一个(也是唯一)的子元素,接下来我们想要在同一个地方再次渲染 <button> 。在宿主实例中我们已经有了一个 <button> 为什么还要重新创建呢?让我们重用它。这与 React 如何思考并解决这类问题已经很接近了。如果相同的元素类型在同一个地方先后出现两次,React 会重用已有的宿主实例。这里有一个例子,其中的注释大致解释了 React 是如何工作的:// let domNode = document.createElement(‘button’);// domNode.className = ‘blue’;// domContainer.appendChild(domNode);ReactDOM.render( <button className=“blue” />, document.getElementById(‘container’));// 能重用宿主实例吗?能!(button → button)// domNode.className = ‘red’;ReactDOM.render( <button className=“red” />, document.getElementById(‘container’));// 能重用宿主实例吗?不能!(button → p)// domContainer.removeChild(domNode);// domNode = document.createElement(‘p’);// domNode.textContent = ‘Hello’;// domContainer.appendChild(domNode);ReactDOM.render( <p>Hello</p>, document.getElementById(‘container’));// 能重用宿主实例吗?能!(p → p)// domNode.textContent = ‘Goodbye’;ReactDOM.render( <p>Goodbye</p>, document.getElementById(‘container’));同样的启发式方法也适用于子树。例如,当我们在 <dialog> 中新增两个 <button> ,React 会先决定是否要重用 <dialog> ,然后为每一个子元素重复这个决定步骤。条件如果 React 在渲染更新前后只重用那些元素类型匹配的宿主实例,那当遇到包含条件语句的内容时又该如何渲染呢?假设我们只想首先展示一个输入框,但之后要在它之前渲染一条信息:// 第一次渲染ReactDOM.render( <dialog> <input /> </dialog>, domContainer);// 下一次渲染ReactDOM.render( <dialog> <p>I was just added here!</p> <input /> </dialog>, domContainer);在这个例子中,<input> 宿主实例会被重新创建。React 会遍历整个元素树,并将其与先前的版本进行比较:dialog → dialog :能重用宿主实例吗?能 — 因为类型是匹配的。input → p :能重用宿主实例吗?不能,类型改变了! 需要删除已有的 input 然后重新创建一个 p 宿主实例。(nothing) → input :需要重新创建一个 input 宿主实例。因此,React 会像这样执行更新:let oldInputNode = dialogNode.firstChild;dialogNode.removeChild(oldInputNode);let pNode = document.createElement(‘p’);pNode.textContent = ‘I was just added here!’;dialogNode.appendChild(pNode);let newInputNode = document.createElement(‘input’);dialogNode.appendChild(newInputNode);这样的做法并不科学因为事实上 <input> 并没有被 <p> 所替代 — 它只是移动了位置而已。我们不希望因为重建 DOM 而丢失了 selection、focus 等状态以及其中的内容。虽然这个问题很容易解决(在下面我会马上讲到),但这个问题在 React 应用中并不常见。而当我们探讨为什么会这样时却很有意思。事实上,你很少会直接调用 ReactDOM.render 。相反,在 React 应用中程序往往会被拆分成这样的函数:function Form({ showMessage }) { let message = null; if (showMessage) { message = <p>I was just added here!</p>; } return ( <dialog> {message} <input /> </dialog> );}这个例子并不会遇到刚刚我们所描述的问题。让我们用对象注释而不是 JSX 也许可以更好地理解其中的原因。来看一下 dialog 中的子元素树:function Form({ showMessage }) { let message = null; if (showMessage) { message = { type: ‘p’, props: { children: ‘I was just added here!’ } }; } return { type: ‘dialog’, props: { children: [ message, { type: ‘input’, props: {} } ] } };}不管 showMessage 是 true 还是 false ,在渲染的过程中 <input> 总是在第二个孩子的位置且不会改变。如果 showMessage 从 false 改变为 true ,React 会遍历整个元素树,并与之前的版本进行比较:dialog → dialog :能够重用宿主实例吗?能 — 因为类型匹配。(null) → p :需要插入一个新的 p 宿主实例。input → input :能够重用宿主实例吗?能 — 因为类型匹配。之后 React 大致会像这样执行代码:let inputNode = dialogNode.firstChild;let pNode = document.createElement(‘p’);pNode.textContent = ‘I was just added here!’;dialogNode.insertBefore(pNode, inputNode);这样一来输入框中的状态就不会丢失了。列表比较树中同一位置的元素类型对于是否该重用还是重建相应的宿主实例往往已经足够。但这只适用于当子元素是静止的并且不会重排序的情况。在上面的例子中,即使 message 不存在,我们仍然知道输入框在消息之后,并且再没有其他的子元素。而当遇到动态列表时,我们不能确定其中的顺序总是一成不变的。function ShoppingList({ list }) { return ( <form> {list.map(item => ( <p> You bought {item.name} <br /> Enter how many do you want: <input /> </p> ))} </form> )}如果我们的商品列表被重新排序了,React 只会看到所有的 p 以及里面的 input 拥有相同的类型,并不知道该如何移动它们。(在 React 看来,虽然这些商品本身改变了,但是它们的顺序并没有改变。)所以 React 会对这十个商品进行类似如下的重排序:for (let i = 0; i < 10; i++) { let pNode = formNode.childNodes[i]; let textNode = pNode.firstChild; textNode.textContent = ‘You bought ’ + items[i].name;}React 只会对其中的每个元素进行更新而不是将其重新排序。这样做会造成性能上的问题和潜在的 bug 。例如,当商品列表的顺序改变时,原本在第一个输入框的内容仍然会存在于现在的第一个输入框中 — 尽管事实上在商品列表里它应该代表着其他的商品!这就是为什么每次当输出中包含元素数组时,React 都会让你指定一个叫做 key 的属性:function ShoppingList({ list }) { return ( <form> {list.map(item => ( <p key={item.productId}> You bought {item.name} <br /> Enter how many do you want: <input /> </p> ))} </form> )}key 给予 React 判断子元素是否真正相同的能力,即使在渲染前后它在父元素中的位置不是相同的。当 React 在 <form> 中发现 <p key=“42”> ,它就会检查之前版本中的 <form> 是否同样含有 <p key=“42”> 。即使 <form> 中的子元素们改变位置后,这个方法同样有效。在渲染前后当 key 仍然相同时,React 会重用先前的宿主实例,然后重新排序其兄弟元素。需要注意的是 key 只与特定的父亲 React 元素相关联,比如 <form> 。React 并不会去匹配父元素不同但 key 相同的子元素。(React 并没有惯用的支持对在不重新创建元素的情况下让宿主实例在不同的父元素之间移动。)给 key 赋予什么值最好呢?最好的答案就是:什么时候你会说一个元素不会改变即使它在父元素中的顺序被改变? 例如,在我们的商品列表中,商品本身的 ID 是区别于其他商品的唯一标识,那么它就最适合作为 key 。组件我们已经知道函数会返回 React 元素:function Form({ showMessage }) { let message = null; if (showMessage) { message = <p>I was just added here!</p>; } return ( <dialog> {message} <input /> </dialog> );}这些函数被叫做组件。它们让我们可以打造自己的“工具箱”,例如按钮、头像、评论框等等。组件就像 React 的面包和黄油。组件接受一个参数 — 对象哈希。它包含“props”(“属性”的简称)。在这里 showMessage 就是一个 prop 。它们就像是具名参数一样。纯净React 组件中对于 props 应该是纯净的。function Button(props) { // ???? 没有作用 props.isActive = true;}通常来说,突变在 React 中不是惯用的。(我们会在之后讲解如何用更惯用的方式来更新 UI 以响应事件。)不过,局部的突变是绝对允许的:function FriendList({ friends }) { let items = []; for (let i = 0; i < friends.length; i++) { let friend = friends[i]; items.push( <Friend key={friend.id} friend={friend} /> ); } return <section>{items}</section>;}当我们在函数组件内部创建 items 时不管怎样改变它都行,只要这些突变发生在将其作为最后的渲染结果之前。所以并不需要重写你的代码来避免局部突变。同样地,惰性初始化是被允许的即使它不是完全“纯净”的:function ExpenseForm() { // 只要不影响其他组件这是被允许的: SuperCalculator.initializeIfNotReady(); // 继续渲染……}只要调用组件多次是安全的,并且不会影响其他组件的渲染,React 并不关心你的代码是否像严格的函数式编程一样百分百纯净。在 React 中,幂等性比纯净性更加重要。也就是说,在 React 组件中不允许有用户可以直接看到的副作用。换句话说,仅调用函数式组件时不应该在屏幕上产生任何变化。递归我们该如何在组件中使用组件?组件属于函数因此我们可以直接进行调用:let reactElement = Form({ showMessage: true });ReactDOM.render(reactElement, domContainer);然而,在 React 运行时中这并不是惯用的使用组件的方式。相反,使用组件惯用的方式与我们已经了解的机制相同 — 即 React 元素。这意味着不需要你直接调用组件函数,React 会在之后为你做这件事情:// { type: Form, props: { showMessage: true } }let reactElement = <Form showMessage={true} />;ReactDOM.render(reactElement, domContainer);然后在 React 内部,你的组件会这样被调用:// React 内部的某个地方let type = reactElement.type; // Formlet props = reactElement.props; // { showMessage: true }let result = type(props); // 无论 Form 会返回什么组件函数名称按照规定需要大写。当 JSX 转换时看见 <Form> 而不是 <form> ,它让对象 type 本身成为标识符而不是字符串:console.log(<form />.type); // ‘form’ 字符串console.log(<Form />.type); // Form 函数我们并没有全局的注册机制 — 字面上当我们输入 <Form> 时代表着 Form 。如果 Form在局部作用域中并不存在,你会发现一个 JavaScript 错误,就像平常你使用错误的变量名称一样。因此,当元素类型是一个函数的时候 React 会做什么呢?它会调用你的组件,然后询问组件想要渲染什么元素。这个步骤会递归式地执行下去,更详细的描述在这里 。总的来说,它会像这样执行:你: ReactDOM.render(<App />, domContainer)React: App ,你想要渲染什么?App :我要渲染包含 <Content> 的 <Layout> 。React: <Layout> ,你要渲染什么?Layout :我要在 <div> 中渲染我的子元素。我的子元素是 <Content> 所以我猜它应该渲染到 <div> 中去。React: <Content> ,你要渲染什么?<Content> :我要在 <article> 中渲染一些文本和 <Footer> 。React: <Footer> ,你要渲染什么?<Footer> :我要渲染含有文本的 <footer> 。React: 好的,让我们开始吧:// 最终的 DOM 结构<div> <article> Some text <footer>some more text</footer> </article></div>这就是为什么我们说协调是递归式的。当 React 遍历整个元素树时,可能会遇到元素的 type 是一个组件。React 会调用它然后继续沿着返回的 React 元素下行。最终我们会调用完所有的组件,然后 React 就会知道该如何改变宿主树。在之前已经讨论过的相同的协调准则,在这一样适用。如果在同一位置的 type 改变了(由索引和可选的 key 决定),React 会删除其中的宿主实例并将其重建。控制反转你也许会好奇:为什么我们不直接调用组件?为什么要编写 <Form /> 而不是 Form()?React 能够做的更好如果它“知晓”你的组件而不是在你递归调用它们之后生成的 React 元素树。// ???? React 并不知道 Layout 和 Article 的存在。// 因为你在调用它们。ReactDOM.render( Layout({ children: Article() }), domContainer)// ✅ React知道 Layout 和 Article 的存在。// React 来调用它们。ReactDOM.render( <Layout><Article /></Layout>, domContainer)这是一个关于控制反转的经典案例。通过让 React 调用我们的组件,我们会获得一些有趣的属性:组件不仅仅只是函数。 React 能够用在树中与组件本身紧密相连的局部状态等特性来增强组件功能。优秀的运行时提供了与当前问题相匹配的基本抽象。就像我们已经提到过的,React 专门针对于那些渲染 UI 树并且能够响应交互的应用。如果你直接调用了组件,你就只能自己来构建这些特性了。组件类型参与协调。 通过 React 来调用你的组件,能让它了解更多关于元素树的结构。例如,当你从渲染 <Feed> 页面转到 Profile 页面,React 不会尝试重用其中的宿主实例 — 就像你用 <p> 替换掉 <button> 一样。所有的状态都会丢失 — 对于渲染完全不同的视图时,通常来说这是一件好事。你不会想要在 <PasswordForm> 和<MessengerChat> 之间保留输入框的状态尽管 <input> 的位置意外地“排列”在它们之间。React 能够推迟协调。 如果让 React 控制调用你的组件,它能做很多有趣的事情。例如,它可以让浏览器在组件调用之间做一些工作,这样重渲染大体量的组件树时就不会阻塞主线程。想要手动编排这个过程而不依赖 React 的话将会十分困难。更好的可调试性。 如果组件是库中所重视的一等公民,我们就可以构建丰富的开发者工具,用于开发中的自省。让 React 调用你的组件函数还有最后一个好处就是惰性求值。让我们看看它是什么意思。惰性求值当我们在 JavaScript 中调用函数时,参数往往在函数调用之前被执行。// (2) 它会作为第二个计算eat( // (1) 它会首先计算 prepareMeal());这通常是 JavaScript 开发者所期望的因为 JavaScript 函数可能有隐含的副作用。如果我们调用了一个函数,但直到它的结果不知怎地被“使用”后该函数仍没有执行,这会让我们感到十分诧异。但是,React 组件是相对纯净的。如果我们知道它的结果不会在屏幕上出现,则完全没有必要执行它。考虑下面这个含有 <Comments> 的 <Page> 组件:function Story({ currentUser }) { // return { // type: Page, // props: { // user: currentUser, // children: { type: Comments, props: {} } // } // } return ( <Page user={currentUser}> <Comments /> </Page> );}<Page> 组件能够在 <Layout> 中渲染传递给它的子项:function Page({ user, children }) { return ( <Layout> {children} </Layout> );}(在 JSX 中 <A><B /></A> 和 <A children={<B />} /> 相同。)但是要是存在提前返回的情况呢?function Page({ user, children }) { if (!user.isLoggedIn) { return <h1>Please log in</h1>; } return ( <Layout> {children} </Layout> );}如果我们像函数一样调用 Commonts() ,不管 Page 是否想渲染它们都会被立即执行:// {// type: Page,// props: {// children: Comments() // Always runs!// }// }<Page> {Comments()}</Page>但是如果我们传递的是一个 React 元素,我们不需要自己执行 Comments :// {// type: Page,// props: {// children: { type: Comments }// }// }<Page> <Comments /></Page>让 React 来决定何时以及是否调用组件。如果我们的的 Page 组件忽略自身的 childrenprop 且相反地渲染了 <h1>Please login</h1> ,React 不会尝试去调用 Comments 函数。重点是什么?这很好,因为它既可以让我们避免不必要的渲染也能使我们的代码变得不那么脆弱。(当用户退出登录时,我们并不在乎 Comments 是否被丢弃 — 因为它从没有被调用过。)状态我们先前提到过关于协调和在树中元素概念上的“位置”是如何让 React 知晓是该重用宿主实例还是该重建它。宿主实例能够拥有所有相关的局部状态:focus、selection、input 等等。我们想要在渲染更新概念上相同的 UI 时保留这些状态。我们也想可预测性地摧毁它们,当我们在概念上渲染的是完全不同的东西时(例如从 <SignupForm> 转换到 <MessengerChat>)。局部状态是如此有用,以至于 React 让你的组件也能拥有它。 组件仍然是函数但是 React 用对构建 UI 有好处的许多特性增强了它。在树中每个组件所绑定的局部状态就是这些特性之一。我们把这些特性叫做 Hooks 。例如,useState 就是一个 Hook 。function Example() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> );}它返回一对值:当前的状态和更新该状态的函数。数组的解构语法让我们可以给状态变量自定义名称。例如,我在这里称它们为 count 和 setCount ,但是它们也可以被称作 banana 和 setBanana 。在这些文字之下,我们会用 setState 来替代第二个值无论它在具体的例子中被称作什么。(你能在 React 文档 中学习到更多关于 useState 和 其他 Hooks 的知识。)一致性即使我们想将协调过程本身分割成非阻塞的工作块,我们仍然需要在同步的循环中对真实的宿主实例进行操作。这样我们才能保证用户不会看见半更新状态的 UI ,浏览器也不会对用户不应看到的中间状态进行不必要的布局和样式的重新计算。这也是为什么 React 将所有的工作分成了”渲染阶段“和”提交阶段“的原因。渲染阶段 是当 React 调用你的组件然后进行协调的时段。在此阶段进行干涉是安全的且在未来这个阶段将会变成异步的。提交阶段 就是 React 操作宿主树的时候。而这个阶段永远是同步的。缓存当父组件通过 setState 准备更新时,React 默认会协调整个子树。因为 React 并不知道在父组件中的更新是否会影响到其子代,所以 React 默认保持一致性。这听起来会有很大的性能消耗但事实上对于小型和中型的子树来说,这并不是问题。当树的深度和广度达到一定程度时,你可以让 React 去缓存子树并且重用先前的渲染结果当 prop 在浅比较之后是相同时:function Row({ item }) { // …}export default React.memo(Row);现在,在父组件 <Table> 中调用 setState 时如果 <Row> 中的 item 与先前渲染的结果是相同的,React 就会直接跳过协调的过程。你可以通过 useMemo() Hook 获得单个表达式级别的细粒度缓存。该缓存于其相关的组件紧密联系在一起,并且将与局部状态一起被销毁。它只会保留最后一次计算的结果。默认情况下,React 不会故意缓存组件。许多组件在更新的过程中总是会接收到不同的 props ,所以对它们进行缓存只会造成净亏损。原始模型令人讽刺地是,React 并没有使用“反应式”的系统来支持细粒度的更新。换句话说,任何在顶层的更新只会触发协调而不是局部更新那些受影响的组件。这样的设计是有意而为之的。对于 web 应用来说交互时间是一个关键指标,而通过遍历整个模型去设置细粒度的监听器只会浪费宝贵的时间。此外,在很多应用中交互往往会导致或小(按钮悬停)或大(页面转换)的更新,因此细粒度的订阅只会浪费内存资源。React 的设计原则之一就是它可以处理原始数据。如果你拥有从网络请求中获得的一组 JavaScript 对象,你可以将其直接交给组件而无需进行预处理。没有关于可以访问哪些属性的问题,或者当结构有所变化时造成的意外的性能缺损。React 渲染是 O(视图大小) 而不是 O(模型大小) ,并且你可以通过 windowing 显著地减少视图大小。有那么一些应用细粒度订阅对它们来说是有用的 — 例如股票代码。这是一个极少见的例子,因为“所有的东西都需要在同一时间内持续更新”。虽然命令式的方法能够优化此类代码,但 React 并不适用于这种情况。同样的,如果你想要解决该问题,你就得在 React 之上自己实现细粒度的订阅。注意,即使细粒度订阅和“反应式”系统也无法解决一些常见的性能问题。 例如,渲染一棵很深的树(在每次页面转换的时候发生)而不阻塞浏览器。改变跟踪并不会让它变得更快 — 这样只会让其变得更慢因为我们执行了额外的订阅工作。另一个问题是我们需要等待返回的数据在渲染视图之前。在 React 中,我们用并发渲染来解决这些问题。批量更新一些组件也许想要更新状态来响应同一事件。下面这个例子是假设的,但是却说明了一个常见的模式:function Parent() { let [count, setCount] = useState(0); return ( <div onClick={() => setCount(count + 1)}> Parent clicked {count} times <Child /> </div> );}function Child() { let [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}> Child clicked {count} times </button> );}当事件被触发时,子组件的 onClick 首先被触发(同时触发了它的 setState )。然后父组件在它自己的 onClick 中调用 setState 。如果 React 立即重渲染组件以响应 setState 调用,最终我们会重渲染子组件两次:*** 进入 React 浏览器 click 事件处理过程 Child (onClick) - setState - re-render Child // ???? 不必要的重渲染Parent (onClick) - setState - re-render Parent - re-render Child 结束 React 浏览器 click 事件处理过程 第一次 Child 组件渲染是浪费的。并且我们也不会让 React 跳过 Child 的第二次渲染因为 Parent 可能会传递不同的数据由于其自身的状态更新。这就是为什么 React 会在组件内所有事件触发完成后再进行批量更新的原因: 进入 React 浏览器 click 事件处理过程 Child (onClick) - setStateParent (onClick) - setState Processing state updates *** - re-render Parent - re-render Child*** 结束 React 浏览器 click 事件处理过程 **组件内调用 setState 并不会立即执行重渲染。相反,React 会先触发所有的事件处理器,然后再触发一次重渲染以进行所谓的批量更新。批量更新虽然有用但可能会让你感到惊讶如果你的代码这样写: const [count, setCounter] = useState(0); function increment() { setCounter(count + 1); } function handleClick() { increment(); increment(); increment(); }如果我们将 count 初始值设为 0 ,上面的代码只会代表三次 setCount(1) 调用。为了解决这个问题,我们给 setState 提供了一个 “updater” 函数作为参数: const [count, setCounter] = useState(0); function increment() { setCounter(c => c + 1); } function handleClick() { increment(); increment(); increment(); }React 会将 updater 函数放入队列中,并在之后按顺序执行它们,最终 count 会被设置成 3 并作为一次重渲染的结果。当状态逻辑变得更加复杂而不仅仅只是少数的 setState 调用时,我建议你使用 useReducer Hook 来描述你的局部状态。它就像 “updater” 的升级模式在这里你可以给每一次更新命名: const [counter, dispatch] = useReducer((state, action) => { if (action === ‘increment’) { return state + 1; } else { return state; } }, 0); function handleClick() { dispatch(‘increment’); dispatch(‘increment’); dispatch(‘increment’); }action 字段可以是任意值,尽管对象是常用的选择。调用树编程语言的运行时往往有调用栈 。当函数 a() 调用 b() ,b() 又调用 c() 时,在 JavaScript 引擎中会有像 [a, b, c] 这样的数据结构来“跟踪”当前的位置以及接下来要执行的代码。一旦 c 函数执行完毕,它的调用栈帧就消失了!因为它不再被需要了。我们返回到函数 b 中。当我们结束函数 a 的执行时,调用栈就被清空。当然,React 以 JavaScript 运行当然也遵循 JavaScript 的规则。但是我们可以想象在 React 内部有自己的调用栈用来记忆我们当前正在渲染的组件,例如 [App, Page, Layout, Article / 此刻的位置 */] 。React 与通常意义上的编程语言进行时不同因为它针对于渲染 UI 树,这些树需要保持“活性”,这样才能使我们与其进行交互。在第一次 ReactDOM.render() 出现之前,DOM 操作并不会执行。这也许是对隐喻的延伸,但我喜欢把 React 组件当作 “调用树” 而不是 “调用栈” 。当我们调用完 Article 组件,它的 React “调用树” 帧并没有被摧毁。我们需要将局部状态保存以便映射到宿主实例的某个地方。这些“调用树”帧会随它们的局部状态和宿主实例一起被摧毁,但是只会在协调规则认为这是必要的时候执行。如果你曾经读过 React 源码,你就会知道这些帧其实就是 Fibers) 。Fibers 是局部状态真正存在的地方。当状态被更新后,React 将其下面的 Fibers 标记为需要进行协调,之后便会调用这些组件。上下文在 React 中,我们将数据作为 props 传递给其他组件。有些时候,大多数组件需要相同的东西 — 例如,当前选中的可视主题。将它一层层地传递会变得十分麻烦。在 React 中,我们通过 Context 解决这个问题。它就像组件的动态范围 ,能让你从顶层传递数据,并让每个子组件在底部能够读取该值,当值变化时还能够进行重新渲染:const ThemeContext = React.createContext( ’light’ // 默认值作为后备);function DarkApp() { return ( <ThemeContext.Provider value=“dark”> <MyComponents /> </ThemeContext.Provider> );}function SomeDeeplyNestedChild() { // 取决于其子组件在哪里被渲染 const theme = useContext(ThemeContext); // …}当 SomeDeeplyNestedChild 渲染时, useContext(ThemeContext) 会寻找树中最近的 <ThemeContext.Provider> ,并且使用它的 value 。(事实上,React 维护了一个上下文栈当其渲染时。)如果没有 ThemeContext.Provider 存在,useContext(ThemeContext) 调用的结果就会被调用 createContext() 时传递的默认值所取代。在上面的例子中,这个值为 ’light’ 。副作用我们在之前提到过 React 组件在渲染过程中不应该有可观察到的副作用。但是有些时候副作用确实必要的。我们也许需要进行管理 focus 状态、用 canvas 画图、订阅数据源等操作。在 React 中,这些都可以通过声明 effect 来完成:function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = You clicked ${count} times; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> );}如果可能,React 会推迟执行 effect 直到浏览器重新绘制屏幕。这是有好处的因为像订阅数据源这样的代码并不会影响交互时间和首次绘制时间 。(有一个极少使用的 Hook 能够让你选择退出这种行为并进行一些同步的工作。请尽量避免使用它。)effect 不只执行一次。当组件第一次展示给用户以及之后的每次更新时它都会被执行。在 effect 中能触及当前的 props 和 state,例如上文例子中的 count 。effect 可能需要被清理,例如订阅数据源的例子。在订阅之后将其清理,effect 能够返回一个函数: useEffect(() => { DataSource.addSubscription(handleChange); return () => DataSource.removeSubscription(handleChange); });React 会在下次调用该 effect 之前执行这个返回的函数,当然是在组件被摧毁之前。有些时候,在每次渲染中都重新调用 effect 是不符合实际需要的。 你可以告诉 React 如果相应的变量不会改变则跳过此次调用: useEffect(() => { document.title = You clicked ${count} times; }, [count]);但是,这往往会成为过早地优化并会造成一些问题如果你不熟悉 JavaScript 中的闭包是如何工作的话。例如,下面的这段代码是有 bug 的: useEffect(() => { DataSource.addSubscription(handleChange); return () => DataSource.removeSubscription(handleChange); }, []);它含有 bug 因为 [] 代表着“不再重新执行这个 effect 。”但是这个 effect 中的 handleChange 是被定义在外面的。handleChange 也许会引用任何的 props 或 state : function handleChange() { console.log(count); }如果我们不再让这个 effect 重新调用,handleChange 始终会是第一次渲染时的版本,而其中的 count 也永远只会是 0 。为了解决这个问题,请保证你声明了特定的依赖数组,它包含所有可以改变的东西,即使是函数也不例外: useEffect(() => { DataSource.addSubscription(handleChange); return () => DataSource.removeSubscription(handleChange); }, [handleChange]);取决于你的代码,在每次渲染后 handleChange 都会不同因此你可能仍然会看到不必要的重订阅。 useCallback 能够帮你解决这个问题。或者,你可以直接让它重订阅。例如浏览器中的 addEventListener API 非常快,但为了在组件中避免使用它可能会带来更多的问题而不是其真正的价值。(你能在 React 文档 中学到更多关于 useEffect 和其他 Hooks 的知识。)自定义钩子由于 useState 和 useEffect 是函数调用,因此我们可以将其组合成自己的 Hooks :function MyResponsiveComponent() { const width = useWindowWidth(); // 我们自己的 Hook return ( <p>Window width is {width}</p> );}function useWindowWidth() { const [width, setWidth] = useState(window.innerWidth); useEffect(() => { const handleResize = () => setWidth(window.innerWidth); window.addEventListener(‘resize’, handleResize); return () => { window.removeEventListener(‘resize’, handleResize); }; }); return width;}自定义 Hooks 让不同的组件共享可重用的状态逻辑。注意状态本身是不共享的。每次调用 Hook 都只声明了其自身的独立状态。(你能在 React 文档 中学习更多关于构建自己的 Hooks 的内容。)静态使用顺序你可以把 useState 想象成一个可以定义“React 状态变量”的语法。它并不是真正的语法,当然,我们仍在用 JavaScript 编写应用。但是我们将 React 作为一个运行时环境来看待,因为 React 用 JavaScript 来描绘整个 UI 树,它的特性往往更接近于语言层面。假设 use 是语法,将其使用在组件函数顶层也就说得通了:// ???? 注意:并不是真的语法component Example(props) { const [count, setCount] = use State(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> );}当它被放在条件语句中或者组件外时又代表什么呢?// ???? 注意:并不是真的语法// 它是谁的…局部状态?const [count, setCount] = use State(0);component Example() { if (condition) { // 要是 condition 是 false 时会发生什么呢? const [count, setCount] = use State(0); } function handleClick() { // 要是离开了组件函数会发生什么? // 这和一般的变量又有什么区别呢? const [count, setCount] = use State(0); }React 状态和在树中与其相关的组件紧密联系在一起。如果 use 是真正的语法当它在组件函数的顶层调用时也能说的通:// ???? 注意:并不是真的语法component Example(props) { // 只在这里有效 const [count, setCount] = use State(0); if (condition) { // 这会是一个语法错误 const [count, setCount] = use State(0); }这和 import 声明只在模块顶层有用是一样的道理。当然,use 并不是真正的语法。 (它不会带来很多好处,并且会带来很多麻烦。)然而,React 的确期望所有的 Hooks 调用只发生在组件的顶部并且不在条件语句中。这些 Hooks 的规则能够被 linter plugin 所规范。有很多关于这种设计选择的激烈争论,但在实践中我并没有看到它让人困惑。我还写了关于为什么通常提出的替代方案不起作用的文章。Hooks 的内部实现其实是链表 。当你调用 useState 的时候,我们将指针移到下一项。当我们退出组件的“调用树”帧时,会缓存该结果的列表直到下次渲染开始。这篇文章简要介绍了 Hooks 内部是如何工作的。数组也许是比链表更好解释其原理的模型:// 伪代码let hooks, i;function useState() { i++; if (hooks[i]) { // 再次渲染时 return hooks[i]; } // 第一次渲染 hooks.push(…);}// 准备渲染i = -1;hooks = fiber.hooks || [];// 调用组件YourComponent();// 缓存 Hooks 的状态fiber.hooks = hooks;(如果你对它感兴趣,完整的代码在这里 。)这大致就是每个 useState() 如何获得正确状态的方式。就像我们之前所知道的,“匹配”对 React 来说并不是什么新的知识 — 这与协调依赖于在渲染前后元素是否匹配是同样的道理。还有哪些遗漏我们已经触及到 React 运行时环境中几乎所有重要的方面。如果你读完了这篇文章,可能已经比 90% 的开发者更了解 React ,没错!当然有一些内容我并没有提到——主要是因为我们也不太清楚。目前 React 对多道渲染的支持并不太好,即当父组件进行渲染时需要子组件提供的信息。错误处理 API 目前也还没有关于 Hooks 的内容。将来这两个问题可能会一起解决。并发模式在目前看来并不稳定,也有很多关于 Suspense 该如何适应当前版本的有趣问题。也许我会在它们要完成的时候再来讨论,并且 Suspense 已经准备好比 懒加载 能够做的更多。我认为 React API 的成功之处在于,即使在没有考虑过上面这些大多数主题的情况下,你也能轻松使用它并且可以走的很远。 在大多数情况下,像协调这样好的默认特性启发式地为我们做了正确的事情。在你忘记添加 key 这样的属性时,React 能够好心提醒你。如果你是一个痴迷于 UI 库的书呆子,我希望这篇文章对你来说会很有趣的,并且深入阐明了 React 的工作原理。也许你会觉得 React 太过复杂,所以不会再去深入理解它。不管怎样,我都很乐意在 Twitter 上听到你的声音!感谢你的阅读。本文首发微信公众号:jingchengyideng欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章 ...

February 14, 2019 · 10 min · jiezi

创建华丽 UI 的 7条规则 第二部分 (2019年更新)

这是这个系列的第二篇,第一篇这见 这里:以下是这个系列的简洁 UI 的 7 条规则:光来自天空 (Light comes from the sky)黑白优先 (Black and white first)加倍你的空白 (Double your whitespace)学习在图像上叠加文本的方法 ( Learn the methods of overlaying text on images )使文本层次分明 ( Make text pop — and un-pop )只使用好看的字体 ( Only use good fonts )像艺术家一样借鉴 ( Steal like an artist )4. 学习在图像上叠加文本的方法在图像上添加吸引人文本方法只那么几种,这里介绍五种常规和一种额外的方法。如果想成为一名优秀的 UI 设计师,必须学会如何以一种吸引人的方式将文本放置于图像之上。每个优秀的 UI 设计师在这个方面都能做得很好,相反的糟糕的 UI 设计师都处理的很差,或者根本不处理。不管你是优秀还是平庸的设计师,阅读这篇文章后,多多少少对你都有帮助。方法一:将文本直接放置于图片上我一直在考虑要不要把这个方法算进五种方法的一种,但设计上,直接将文字放置于图片上让视觉效果更好是可行的。这种方法有各种各样的问题和需要注意事项:图像色调应该偏暗,并且竖直方向上不能有太大的色差。文本必须是白色的。测量不同尺寸的屏幕或窗口以确保图像显示正常。我想我从来没有在任何专业项目中直接在图像上使用文本,之所以提到它,是把它看做是一种应该掌握的技巧,就是说这种方法虽然可能可以产生非常酷炫的效果,但使用的时候需要小心方法二:文本覆盖整个图像将文本放在图像上最简单的方法就是用遮罩将图片整个覆盖,如果原始图像不够暗,可以在整个图像上添加半透明的黑色图层。下图是一个时下流行的、用半透明黑色遮罩覆盖图片的示例。如果打开发工具并删除覆盖层,将看到原始图像太亮,对比度太大,文本难以辨认。但是用黑色半透明的图层覆盖,看上去就没问题了!这个方法用在缩略图和小的图片上同样好用。虽然黑色覆盖是最简单和最通用的,当然也有用彩色覆盖。方法三:盒模型中的文本这种方法简单又可靠。试试把一个稍微透明的黑色长方形框里放上一些白色的文字。如果图片的不透明度(opaque)足够,你可以使用任意一张图片,都可以保证文字的清晰可读。当然也可以使用一些颜色,只是在选择色彩时候要有依据。方法四:模糊图片使文本内容清晰的一个神奇的方法,是将背景图像的一部分变得模糊。苹果确实让背景变得模糊了,尽管它是在 Windows 系统中最先实现的。你也可以用照片的散焦(out-of-focus)部分来作为模糊区域。但是请注意 —— 这个办法并不好使。如果图片做了一点改变,就得确保这些文字位置在对应的模糊区域中。请阅读下图中的子标题:方法五:Floor Fade Floor Fade 指的是图片靠近底部的地方逐渐变黑,然后接着把白字填在上面。这是个非常巧妙的办法,我不知道是谁发明的,在Medium使用它之前,我从未见过有人用它。对于上面的图像,你可能会觉得就是直接在图像上放置了白色的文字,其实不然,你仔细看,你会发现其实是一个由 0% 不透明度到20%不透明度渐变的矩形框。这种渐变效果确实很难看出来,但确实是有的,绝对改善清晰度。还要注意的是,这几张缩略图使用了文本阴影来进一步增强可读性,这个做法真棒!Medium达到了这样的境界:任何文字放置在任何图片上,都能获得良好的阅读效果。哦,还有一件事——为什么图像底部逐渐变暗? 关于这个问题的答案,上篇讲的规则1——灯光通常是从上面照下来的。为了让我们的眼睛看起来更自然,图像的底部稍微暗一点,就像我们所见过的其他事物一样。更高级的做法,就是结合模糊化,这样的结合就是底部模糊化了。额外的办法:纱罩无论背景图像怎么变,Elastica blog的标题总是清晰易读的,这是怎么做到的?应该是这样:并不是特别黑的有一点高对比度然而,很难描述为什么文本如此易读。 看一看:答案是:纱罩。纱幕是一种使光线更柔和的摄影器材。现在它也是一种视觉设计技术,用于软化图像,使叠加的文本更清晰。在浏览器放大 Elastica 博客上,就可以更清楚地做了什么效果。在这句标题 “145,000 Salesforce Users Come out to Celebrate…”有一个让透光度渐变的框。应该可以很简单的注意到高对比度的照片下这个深蓝色的背景。这可能是在图像上可靠地叠加文本的最微妙的方式,我在其他任何地方都没有见过(但它相当隐蔽)。不过要记下来,你或许在将来某些时候需要它。5. 使文本层次分明让文本看起来美观和合适通常做法的是以对比的方式设置样式 - 例如,更大但更轻。在我看来,创建一个漂亮的用户界面最困难的部分就是文本的样式 - 当然不是因为不熟悉这些属性。 如果你刚小学毕业,那么你很可能已经使用了一种方法来引起注意或远离我们看到的文本:尺寸(大或小)颜色(反差较大或较小;色彩鲜明)字重(加深或者变轻)拼写(小写,大写和标题的格式)斜体字母间距边距还有一些其做法可以引起别人的注意,通常不常用也不推荐使用:下划线 –下划线默认表示链接,除了链接外也没必要用它。文本的背景色 – 不常见,但37 signals的网站曾使用它做为链接的样式。删除线 – 90年代的CSS用法了根据我的个人经验,当我发现一个我似乎无法找到合适的文本样式时,并不是因为我忘了尝试使用边距或更暗的颜色 - 而是因为最好的解决办法是同时设置几组“相矛盾的(competing)”属性。Up-pop and down-pop可以将设计文本的所有方式分为两组:增加文本可见性的样式。大号字体、粗体、大写的等等。降低文本可见性的样式。小号字体,对比度小的,边距小的,等等。我们会这些叫做 “up-pop” 和 “down-pop” 的样式,以纪念 favorite adjective。“Material Design” 的标题有很多“up-pop”。大字号,强烈对比,粗体。底部的元素就是“down-pop”的。字体小,对比度低,并且字体较细。以下是非常重要的内容。这个页的标题是仅有的用上了所有 up-pop 方法的文本。 对于所有别的东西,你需要 up-pop 并且 down-pop。如果需要强调一个网站的内容元素,那么就同时使用“up-pop”和“down-pop”。这是为了防止元素过于突兀,将不同元素限制在它们应有的视觉重要性之内。完美设计的 Blu Homes 网站有一些大标题,但是需要强调的单词都是小写的——过多的强调看起来会让人看不到重点。Blu Homes 网站上的这些数字以它们的大小、颜色和对齐方式吸引你的眼球,但是请注意,它们同时被淡化了,字体很轻,低对比度的颜色。然而,数字下面的小标签虽然是灰色和小字体的,但也是大写字重大的。这一切构成了平衡。Contents Magazine 是一个 up-pop 和 down-pop 很不错的案例分析。文章标题基本上是惟一的非斜体页面元素。在这种情况下,缺乏斜体字会更有效地吸引眼球(特别是结合粗体的字体)在 by 的这一行里的作者名字是被加粗的 — 让它和平常字重的 “by” 分别了开来。小的、低对比度的“已经过时”文本不会碍手碍脚——但是由于它的大写类型、大的字母间距和大的空白,你可以在查找时立即看到它。选中和鼠标停留样式被选中和鼠标停留的文本样式是另外一回事了——并且很难。通常,改变字体大小、大小写或字体权重会改变文本占用的区域大小,这种变化可以限制住悬浮效果。所以还有哪些属性可以更改呢?字段颜色背影颜色阴影下划线轻微的动画 - 升高,降低等一个实用的办法:尝试将白色元素变成彩色,或者将彩色的元素变为白色,但是文本的背景色要选用深色。设计文本的样式是很难的。最后我还是要告诉你,给文本加样式是很难的。如果你想学习更多关于文本样式的知识,请查看学习UI设计,在这里有更多的详细介绍。6. 使用好看的字体有些字体不错,使用这些字体。本节没有策略或内容需要学习,只列出一些不错的免费字体供你下载和使用。这份学习指南是给学习者的,外面有超多免费的字体,所以就让我们用吧。我建议大家现在就去下载它们,然后使用它们来对你的项目进行可视化设计。以下推荐字体跟级别没有关系:1. Work Sans有时候正在设计一些需要现代,干净字体的东西,但是还要有一点乐趣。 Work Sans 非常适合这种场景。 下载地址:没包含斜体的 有包含斜体的。 2. Roboto一种极好、干净的、通用的字体。虽然它是 Android 的默认字体,但对于 iPhone 和 web 应用程序来说,仍然没有得到充分的使用主要还是免费的!谷歌地图有用到该字体下载地址请点击这里。3. Montserrat我曾经犹豫是否推荐 Montserrat 字体,因为它没有斜体字,字距怪异,而且厚得很难看)。但这个项目一直很活跃,Montserrat 变成了一种不可思议的字体。它最有名的可能是最受欢迎(和精心设计的)Proxima Nova 的最佳免费替代品。在选择任何字体时,最好查看大写、句子大小写和所有的字重。你永远不知道什么时候稍微不同的设置会成为你想要的风格。比较上面的两个镜头——同样的字体,两种不同的感觉。下载地址点击这里。4. Source Sans Pro我喜欢 Source Sans 的一件事是当你想要使用令人难以置信的过度使用的 Open Sans 或 Lato 时,它是一个很好的选择。Source Sans与 Open Sans或 Lato - neutral 字符有许多相同的优点,只是有一点人性化(而不是冷冰冰的、生硬的几何字体),而且对于用户界面非常有用。可以在 Google Fonts 上找到5. IBM Plex Sans去年,IBM 发布了自己的字体 Plex。Plex Serif 和 Plex Mono 轻松配对。可以在 Google Fonts 上找到6. Feather Icons虽然许多流行的图标集(ahem,Font Awesome)具有过于圆润和起泡的形状,但与简洁的设计不能很好地搭配,但是 Feather Icons 是一种非常不受欢迎的解决方案。作者还没有把它打包成图标字体,但是有人在 Github 上放了一个字体版本,可以很好地跟踪原始设置(如果你只使用了套装中的10或20个图标,没有必要加载整个包)。下载地址:SVG set, partial icon font有一些资源:Beautiful Google web fonts。这个网站非常棒得展现出 Google Fonts 能有多好看。作者从它那找了好多好多次灵感。FontSquirrel。一组最好的字体可供商业使用,而且完全免费。Adobe Fonts。如果你使用的是 Adobe Creative Cloud( 即订阅 Photoshop 或Illustrator等 ),那么可以免费访问大量专业字体。甚至比我上面推荐的还要好:Proxima Nova,Adelle Sans,DIN,Freight Text 等等。Learn UI Design。寻找更好的字体?作者的用户界面设计课程有一个字体推荐列表,包含超过 60 种免费,涵盖所有类型的字体(衬线,平板,等宽字体,手写等),并包括每个字体的注释 字体效果最好。7. 像艺术家一样借鉴我第一次尝试设计一些应用程序元素 - 按钮,表,图表,弹出窗口 - 这是我第一次意识到我对如何让这些元素好看而知之甚少。但幸运的是,我还没有发明任何新的 UI 元素。这意味着我总能看到别人是如何做到的,并从中挑选最好的。但是我们要从哪里挑呢?这里有:1.Dribbble这个特邀的“给设计师展示”网站有网络上最好质量的 UI 设计作品。你可以在这里找到几乎最好的网站。事实上,你可以在 Dribble 关注我的里面的作品,这里还有一些人你也可以关注:Jamie Syke。基本上每天都发布新的UI,一些流行的东西回归丰富的经验和设计。我能说什么呢?关注就对咯。Balkan Brothers。似乎是一个老生常谈的说法,设计师越接近俄罗斯,他们就越擅长颜色。 这些克罗地亚设计师非常棒,保持平淡有趣。 总是很棒的渐变,颜色和阴影。Elegant Seagulls。如果你曾经想过“天哪,我怎么做比标准网格更有趣的事情?”,浏览他们的一些照片,这里有你想要的答案。Cosmin Capitanu。一个非常厉害的多面手。他做得东西未来感十足,但又不过于高调。他非常善于使用颜色,然而他并不十分注重 UX 的东西 — 当然这个批评也针对 Dribbble 这个网站。分别来自 Balkan Brothers 和 Cosmin Capitanu。分别来自 Elegant Seagulls 和 Jamie Syke。2.Flat UI Pinboard我不知道“warmarc”是谁,但是他的手机UI的pinboard让我找到了许多漂亮的UI。3. Pttrns一个移动app屏幕截图的汇总。Pttrns 的好处是整个网站都是由用户体验模式来组织的。这使得快速研究目前正在使用的任何界面,无论是登录页面、用户配置文件、搜索结果等等,都非常方便。我坚信每个艺术家都应该像鹦鹉一样去模仿,直到他们擅长模仿最好的。然后去寻找你自己的风格,发明新的潮流。在这期间,让我们都先当一个模仿者吧。总结我写这篇文章是因为我希望自己在以前可以读到这篇。希望对你有帮助。如果你是一个用户体验设计师,画好线框图后做一个漂亮的模型。如果你是一名开发人员,那就把你的下一个次要项目做好。我不想UI只有专业的人才能做的很好。就是观察、模仿和记录有用的东西。无论如何,这就是我到目前为止所学到的,同时我永远都是一个学生,会不断向别学习!你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

February 11, 2019 · 2 min · jiezi

创建华丽 UI 的 7条规则 第一部分 (2019年更新)

简介首先也是比较重要的,先说明点这篇指南并不适合所有人,主要适合以下从业者:开发者希望能够在必要时设计出自己漂亮的 UI。用户体验设计师希望他们的产品组合看起来比五角呆板的 PPT 更好看或者让用户得到更好的用户体验。本文中主要围绕以下 7 规则讲解:光来自天空 (Light comes from the sky) 黑白优先 (Black and white first)加倍你的空白 (Double your whitespace)学习在图像上叠加文本的方法(Part 2) (Learn the methods of overlaying text on images)使文本层次分明 (Part 2)( Make text pop — and un-pop ) 使用好看的字体 (Part 2)(Only use good fonts)像艺术家一样借鉴 (Part 2)(Steal like an artist)规则一: 光来自天空 (Light comes from the sky)大脑在理解我们看到的界面时,影子是至关重要的因素。这可能是关于 UI 设计最重要又容易被忽视一个内容:光来自天空。 光线来自天空,从上往上,以至于从下往上的光让人看起来很怪异。当光从天空而来时,它照亮事物的顶部,并在其下方投射阴影,物体的顶部比较亮,底部比较暗。你不会希望人们的下眼睑都特别的黑吧,所以,如果我们在这些恶魔般的眼睛上面多加一些光亮,突然间他们就变成了你家门前的魔鬼女郎。UI 也是一样,正如我们在所有的面部特征的下侧都有少量的阴影,大量 UI 元素的底面也有阴影。我们的屏幕是平的,但我们已经投入了大量的艺术创作让元素富有 3D 效果。拿按钮举例,即使有了这个相对 “平面” 的按钮,仍然有一些与光线相关的细节:未点击的按钮(顶部)底部具有黑色的底部边缘,正如夏天中午的,我们站在太阳时影子的样子。未点击的按钮顶部的 亮度略高于底部。这是因为它模仿了一个稍微弯曲的表面,就像你需要把面前的镜子倾斜才能看到太阳一样,倾斜的表面会把更多的阳光反射到你身上。未点击的按钮投射出一个稀薄地阴影——在放大的截图中能看的更清楚。点击后的按钮,底部依然比顶部还要暗一些,并且整个按钮全都更暗。这是因为它与屏幕本身处于同一个平面,光线就不能轻易的照到它了。有人可能会说,我们在现实生活中看到的所有按键都是暗的,因为我们的手去按按钮时挡住了光线。这只是个按钮而已,就已经呈现了4个细微的光线效果,我们现在要把光线理论用在所有地方。iOS 6已经过时了,但它在轻度行为方面提供了一个很好的案例研究。这是 iOS 6的两个设置—— “请勿打扰” 和 “通知”,看看它们有多少光线效果。嵌套控制面板的上边缘投射一个微小的阴影* “ON” 滑块轨道也跟着设置了一些阴影* “ON” 滑块表面是凹的,底部会反射更多光线顶部的边框颜色比较其它的深点,这代表一个垂直于光源的表面,因此接收到大量的光,因此将大量的光反射到你的眼睛中,导致周围会变暗点。常见向内凹陷的视觉元素:文本输入框点击后的按钮滑块单选按钮(未选中)复选框常见向外突出的视觉元素:按钮 (未点击)滑块按钮下拉控件卡片选中的单选按钮弹框扁平化设如何扁平化设计是一种视觉风格,其中的元素缺乏模拟的凹痕或凸出,它们只是纯色的线条和形状。我和其他人一样喜欢干净和,但我不认为这是一个长期的趋势。如何将我们的界面用 3D 来在细微处进行模拟的更加自然,似乎很难将这种做法完全放弃。五年前,我预测我们将会看到“扁平设计”的兴起,至少在 2019 年,这就是我们的现状——扁平干净外观的元素,加上一层阴影,帮助更加直接看到我们所想要看到的内容。在平面设计中,当点击元素时,可以适当加些阴影效果增强体验。扁平化设计的另一个例子:谷歌的 Material Design language。这才是我身边最常出现的事物,它使用微妙的现实世界的线索来表达展示事件特征。也不能说它完全没有模拟真实世界,但是这不同于 2006 年的网页设计风格,并没有使用材质,渐变和光泽的情况出现。我认为扁平化是未来的一种趋势。规则二:黑白优先 (Black and white first)在添加颜色之前先进行灰度化设计可以简化视觉设计中最复杂的元素——并迫使用户关注元素的间距和布局。最近用户体验设计师们热衷于“移动优先”的设计。这意味着,在 Retina 屏幕中,得想象页面上的交互在一个手机上是否行得通。这种限制是有好处的,这有助于简化思想。从较难的问题开始(在小屏幕上可用的应用程序),然后采用更容易的问题的解决方案(在大屏幕上可用的应用程序)。这里有另一个类似的结束:黑白优先。首先是在没有色彩的帮助下让应用变得美观并且可用,最后添加色彩,仅此而已。Haraldur Thorleifsson 的灰度线框图看起来和其他设计师的成品网站一样好。这是一个可靠和简单的方法,可以让应用程序看起来 “干净” 和 “简单”。在过多的地方使用过多的颜色很容易搞砸设计的简单和干净。黑白优先 迫使你首先关注空间、大小和布局,这些都是简洁设计的主要关注点。在有些情况下,黑白优先没有那么有用。那些具有强烈的特定主题的设计——“运动”、“华丽”、“卡通”等等——需要一个能很好地运用色彩的设计师。但是大多数应用除了干净和简单之外,并没有特别强烈的需求属性。这些特定需求的设计难度也大得多。对于其他的设计来讲,都是黑和白优先原则步骤 2:怎么添加颜色最简单的添加颜色是需要一种色调的。在灰度网站上添加一种颜色可以简单有效地吸引眼球。同样可以采取更深的一步。灰度 + 两种颜色,或者灰度 + 单一色调的多种颜色。什么是色调web 通常将颜色称为RGB十六进制代码,RGB 并非在设计中实现颜色的最优框架,更有用的是 HSB(H 代表色调,S 代表饱和度,B 代表亮度)(与HSV 同义,与 HSL 类似)。HSB 比 RGB 更好,因为它符合我们对颜色自然的看法,并且可以观察到 HSB 值的变化所给你看到颜色来带的影响。如果 HSB 对你来说是个新的东西,这里 HSB 颜色的 优质入门文章。《Smashing》 杂志的金色主题。《Smashing》 杂志的蓝色主题。通过修改单一色调的饱和度和亮度,可以生成多种颜色——暗色调、灯光、背景、重点、吸引眼球的特效——而且不会让人眼花缭乱。使用一种或两种基本色调的多种颜色是强调和中和元素的最可靠的方法,而且不会使设计变得混乱。倒数计时器来自 Kerem Suer。关于颜色的其他一些补充色彩是视觉设计中最复杂的领域。虽然很多关于色彩的东西在你完成设计时并不是很实用,但是我却看到了一些非常有用的东西:* 学习 UI 设计:这是作者创建的一门课程,包含3个小时的彩色设计视频(以及 20 多个小时的 UI设计主题视频),观看地址 learnui.design。* 设计色彩学:一个实用的框架。永远不要使用黑色 (伊恩·斯托姆·泰勒):这篇文章谈到完全平面化的灰色几乎从来没有出现在现实世界中,同时它也提到了如何饱和灰色阴影 — 尤其是深色阴影 — 为设计增添了视觉丰富性。另外,饱和的灰色其实更贴近现实世界,这是它最美的地方。Adobe Color CC:一个非常棒的工具,用于查找、修改和创建配色方案。Dribbble search-by-color: 看看世界上最好的设计师正在使用什么颜色设计。规则三:加倍你的空白 (Double your whitespace)在规则 2 中,黑色优先 迫使设计师在考虑颜色之前考虑间距和布局,接下来谈谈间距和布局了。如果你从头编写 HTML 代码,那么你可能熟悉默认情况下 HTML 在页面上的布局方式。基本上,所有东西都挤在页面的顶部。字体很小,行与行之间没有空格,段落之间有一小段空白,但不多。段落一直延伸到页面的末尾,不管是 100px 还是 10000 px。从美学角度来说,这太糟糕了,如果你想让 UI 看起来像设计好的,需要增加很多空白的间距。以下是 Piotr Kwiatkowski 的音乐播放器概念图。特别要注意左边的菜单。菜单项之间的垂直空间是文本本身高度的两倍,上面和下面有同样多的内边距。或者看看列表标题。“播放列表” 和下划线之间有 15px 的空间。这比字体本身还要高,更别提每个列表之间间隔了 25 个像素了。顶部的导航条有更多的空间。文字“搜索音乐”占了整个导航条高度的20%。图标也使用了类似的高度。左边栏的文字之间留出了比较充裕的空间,甚至更多。Piotr 认真考虑在这里增加更多的空白,并且效果很好。尽管这只是它为了更多乐趣(据我所知),就美学而言,它非常漂亮,能够和市面上最好的音乐播放器UI界面相提并论。适当的空白可以让一些最混乱的界面看起来更吸引人、更简单,就像论坛一样。或者维基百科你会发现对此有很多争论,比如说,维基百科的重新设计舍弃了一些关键的网站的功能,但是你不得不说这是一个很好的学习方式!在你的线条之间预留空间。在你的元素之间预留空间。在你的元素组之间预留空白。要第二部分继续讨论:4、学习在图像上叠加文本的方法(Part 2) (Learn the methods of overlaying text on images)5、使文本层次分明 (Part 2)(Make text pop — and un-pop)6、只使用好看的字体 (Part 2)(Only use good fonts)7、像艺术家一样借鉴 (Part 2)(Steal like an artist)你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》! ...

February 11, 2019 · 1 min · jiezi

SAP UI5和微信小程序对比之我见

今天继续由SAP成都研究院著名的菜园子小哥Wang Cong,给大家分享他作为一个SAP前端人员是如何看待SAP UI5和微信小程序的异同点的。关于Wang Cong种菜的手艺,大家请移步到他以前的文章 SAP成都研究院非典型程序猿,菜园子小哥:当我用UI5诊断工具时我用些什么 去观摩,这里不再赘述。下面是他的正文。*近几年微信小程序发展之势如火如荼,越来越多的用户放弃原生App,转而投入小程序的怀抱,大有"一个微信行天下"的趋势。面对如此巨大的流量机遇,百度、阿里等公司也纷纷在自家的核心产品中推出小程序功能,欲与腾讯的微信在这场入口大战中一较高下。至少在今天看来,微信小程序的生态圈依然是其中最为繁荣的。同为"前端框架",SAP UI5与微信小程序有着诸多异同点。这里我们摘取其中本人觉得比较有特点的方面进行对比,看看二者相似的表象下隐含着哪些设计理念上的区别。本文仅代表作者个人作为一个前端开发人员的个人观点。UI5是SAP开发的一套开源的前端框架,而微信小程序则是局限在微信内部,表现形式类似于普通App。虽然两者的核心(或者说大部分功能)都是与用户进行交互,但从架构的角度看,存在很多本质上的不同,我们可以从接口的角度窥见一二。入口UI5不依托任何平台,通过UI5实现的页面可以从多种入口进行访问,基本上只要支持浏览器功能的平台,都可以访问UI5页面。而微信则是微信小程序的唯一入口。后端UI5是一个纯粹的前端框架,对于后端没有做任何的限制,同时也没有任何的支持。微信小程序不但允许你自由地选择后端,而且提供了一些基础的后端支持,在很多情况下开发者甚至无需搭建自己的服务器,就能满足需求。这些支持有:(1) 数据库:微信小程序提供远端的类似MongoDB的JSON数据库支持,用户不需要购买数据库,也不需要任何复杂的前期配置,就能在小程序中直接对JSON数据库进行增删查改等操作。(2) 存储:类似于上面提到的JSON数据库,用户可以在微信小程序中直接利用免费且免配置的远端存储空间来存储文件。(3) 云函数:除了数据库和存储之外,微信小程序还提供了后端逻辑的支持。云函数可以理解成一个功能有限的后端服务器,也可以理解成一个运行在云端的JavaScript方法。优点是方便,一键部署而且免费。缺点是功能有限,无法实现复杂的后端功能。以上功能全部免费免配置,如果发现免费的配额不够,可以申请提升配额或考虑自己搭建服务器。访问限制作为开放的框架,UI5对于外部访问没有做任何限制。而微信小程序则有着严格的审核机制,首先要访问的链接必须是https的安全链接,其次地址必须提交给微信进行审核,审核过后还需维护在小程序后台的访问列表当中。如果上述步骤没有做好,就连腾讯自己的官网都无法访问。其实这样做的原因也很容易理解:用户通过微信小程序访问的所有链接,最初的入口都是微信本身,这也是微信为了自身生态安全而做的必要控制。但这项限制在本文发稿时为止还不是特别完善。因为云函数尚未对访问做限制,开发者可以使用云函数作为路由,访问未经审核的链接。从上面和下面两张图中我们可以看出UI5应用和微信小程序在接口方面的区别,其中虚线框内分别为UI5和微信小程序框架内所提供的功能范围。技术细节一个熟悉SAP UI5的前端开发人员,上手微信小程序应该没有什么难度。两者同为前端框架在设计上自然有很多相似的地方。例如:(1) 在微信小程序中的app.js极其类似于UI5中的component.js,都代表整个应用的一个全局实例。某些作用范围为全局的方法或数据都可以存于其中。(2) 两者在数据绑定方面,都支持灵活的表达式绑定,将更多的本应出现在controller层的逻辑向前推放到view层当中,简化逻辑层。(3) 两者都支持列表渲染,例如UI5中的ListBase中的items属性,而微信小程序中则是通过wx:for属性实现同样的功能。(4) 两者都支持自定义控件/模板,UI5有component和custom control,微信小程序有component和template。但两者也有很多技术上的区别,例如:(1) 前文提到的列表渲染,UI5仅支持对列表类控件的子控件进行列表渲染。而微信小程序则显得更加灵活一些,任何一个控件都可以通过wx:for属性进行重复渲染。例如上面例子中被重复渲染的就是image元素。(2) 除了列表渲染,微信小程序更支持wx:if的条件渲染。即if条件检测的结果为true时渲染,为false时则忽略。UI5中实现类似功能则更多是通过控制visible属性来进行。(3) Routing的实现。两者都是使用栈的方式记录跳转的历史,但是与UI5的"宽容"不同,微信小程序最多仅支持5层跳转。如用户需要继续向下访问,则必须通过其他workaround实现,例如通过redirectTo将栈顶的旧页面弹出栈,换成新页面。(4) 数据绑定方面。UI5的数据绑定功能极其强大,支持各种类型的数据模型的排序和筛选,并且提供双向绑定和单向绑定多种绑定方式。另外数据在view层和controller层的反馈也更加积极。关于SAP UI5和Angular数据绑定的比较,可以参考Jerry这篇文章:https://blogs.sap.com/2016/06…关于SAP UI5和Vue数据双向绑定的实现区别,可以参考Jerry这篇文章:https://blogs.sap.com/2017/06…相对而言微信小程序的数据绑定功能要稍弱一些。首先不提供排序和筛选功能。另外反馈也不够积极:view层改动数据模型需要借助触发事件来调用controller层中的方法进行手动赋值;controller层在更改模型时也必须通过setData方法,才能让改动在view层中生效。(5) 微信小程序的底层是"巨人"微信, 因此可以借助微信方便地调用很多硬件以及软件API,例如:NFC,WIFI,蓝牙,微信运动,生物认证,二维码,登陆以及支付功能等。(6) 纵观两者的控件库,我们可以发现UI5可谓大而全,就连一个表格都要提供responsive table,grid table,smart table等, 目的就是为了支持尺寸各异的不同设备以及各种业务场景。而微信小程序则极其专注在移动端的常用控件,轻量,简易且统一。理念综合以上的比较,我们可以大致发现UI5和微信小程序自面世起便肩负着不同的使命。UI5是SAP为其自研的企业管理软件前端页面所设计的前端框架,以此来实现SAP推荐的Fiori风格的前端应用。它的出现体现了SAP对于确保未来产品拥有统一风格,友好界面和良好用户体验的决心。而微信小程序虽然小,却足以彰显微信扩张的雄心:通过丰富的前后端支持以及简易的上手体验,实现生态圈的急速扩充。而入口和外部访问的限制则是快速扩张的同时,仍然恪守的那份理智。轻量,小巧,快速,简易,移动,一站式。微信想要对你说的是,你的生活,被我承包了。关于SAP UI5和微信小程序,SAP成都研究院的开发人员们做过很多研究,如果您想进行更多阅读,可以参考下面的链接:(1) 我的同事,SAP成都研究院大卫哥Wu David的文章:SAP C4C中国本地化之微信小程序集成(2) 以前Jerry写的SAP UI5框架代码自学教程(3) Jerry在SAP社区上发表过的59篇SAP UI5相关的文章合集(4) Jerry和您聊聊Chrome开发者工具:关于Chrome开发者工具一些搞笑的故事(5) Jerry通过自己调试的方式解决过的UI5的问题合集:https://blogs.sap.com/2016/04…(6) Jerry日常工作中使用Chrome开发者工具积累的一些技巧:https://blogs.sap.com/2016/03…(7) Jerry的碎碎念:SAPUI5, Angular, React和Vue(8) Yang Joey的文章:SAP Cloud for Customer 使用SAP UI5的独特之处(9) 我自己的文章:当我用UI5诊断工具时我用些什么(10) Jerry的文章:在Kubernetes上运行SAP UI5应用(11) Jerry的文章:SAP Fiori + Vue = ?要获取更多Jerry的原创文章,请关注公众号"汪子熙":

February 3, 2019 · 1 min · jiezi

Vue UI框架库开发介绍

本文大家了解如何开发一个Vue UI框架。Vue UI框架demo 源码地址: https://github.com/xubaodian/… 。平时在项目中,我们经常会使用Element UI,Bootstrap Vue等Vue UI库协助开发,这些UI库提供的常用组件可以使我们迅速开发出原型系统,极大地提升开发效率。在开发的过程中,我相信很多人其实都被这两个问题困扰。1、伪组件化我们知道,组件化开发的目的是解耦功能,提高代码复用率和开发效率,进而加快项目开发周期与产品发布速度。如果我们仅仅是把页面分成几个部分,各自为政,这其实不是组件化开发。因为在项目里,多个页面之间大部分时候,能提取很多公共组件出来(文件上传,搜索框,时间输入,工具栏等等),如果这些组件每个开发人员都实现,无疑是浪费时间的。2、项目间组件管理麻烦假如我们新的项目需要大量用到其它项目已实现的组件,很多人会把其它项目的组件直接复制过来,这其实是不利于组件管理的,因为组件代码在多个项目中都有,假如这个组件实现是有缺陷的,我们必须在多个项目中进行修改。如何这种问题经常存在,而且项目又多,给代码管理带来很多工作。这些问题有什么好的解决办法呢?没错,就是开发一个自有的UI库,很多人肯定有过这样的想法。这即有利于前端的组件积累,也有利于组件复用和管理。本文就介绍如何开发Vue UI库。开发Vue UI库,有两个问题是关键:1、项目中如何调用引入的UI库?2、在项目中如何使用UI库中的组件或常用方法?针对这两个问题,我解释下实现思路。1、项目中如何引入自有的UI库?我们平时引入Element UI,Bootstrap Vue等UI库时,一般是使用npm直接安装依赖即可。公司自有的UI库一般不可能发布到npm仓库。但是,npm可以直接将git仓库上的项目,本地文件夹直接安装到项目中,具体方式如下:npm install git+https://github.com/xubaodian/Wstl-UI.git这样就可以把我们私有git仓库上的UI库作为依赖安装到项目中了,至于SVN仓库上的库是否可以安装,我没试过,大家可以尝试一下。对于第一个问题,解决方案就出来了:建立统一的UI组件库,放在公司私有git仓库上,将各个项目中可以抽象出来的组件都统一放在这里,项目中使用的话,直接通过npm 安装即可2、在项目中如何使用UI库中的组件或常用方法?在说解决方案之前,我们先看下Element UI如何在项目中使用的,代码如下:import Vue from ‘vue’;import ElementUI from ’element-ui’;import ’element-ui/lib/theme-chalk/index.css’;import App from ‘./App.vue’;Vue.use(ElementUI);通过上述代码,就可在项目中使用Element UI的组件了,这其实是使用了Vue提供的插件开发技术。我已自己开发的demo,来讲解组件是如何注册到Vue中的。引用自己的demo,上述代码就如下:import Vue from ‘vue’;import App from ‘./App.vue’;//引入UI库js文件,npm安装的依赖,直接引入,指向文件在依赖项目的package.json定义的,该文件的main定义这个引用位置import WsltUI from ‘wstl-ui’;//引用组件库CSS文件import ‘wstl-ui/lib/index.css’;//注册组件库Vue.use(WsltUI);demo的目录如下:这其实是利用了Vue的插件开发技术,插件技术的官方教程地址如下:https://cn.vuejs.org/v2/guide… 。解释下如何使用,分为3个步骤。1、开发组件,组件代码如下,例如,下面是一个带显示面板的搜索框,类似百度搜索框那样:<template> <div class=“wstl-search”> <input type=“text” class=“wstl-search-input” @focus=“getFocus” @blur=“focusOut” @input=“inputChange” v-model=“value”> <div class=“wstl-search-panel” v-if=“panel && showPanel”> <ul> <li v-for="(item, index) in list" :key=“index” @click=“handleClick(item)">{{label ? item[label] : item}}</li> </ul> </div> </div></template><script>export default { name: ‘wstl-search’, props: { //是否开启panel panel: { type: Boolean, default: false }, //搜索面板展示内容 list: { type: Array, default: () => [] }, label: { type: String, default: ’’ }, value: { type: String, default: ’’ } }, data() { return { showPanel: false } }, methods: { inputChange() { this.$emit(‘input’, this.value); }, getFocus() { this.showPanel = true; }, focusOut() { setTimeout(() => { this.$nextTick(() => { this.showPanel = false; }); }, 200); }, handleClick(item) { this.$emit(‘select’, item); } }}</script><style lang=“less” scoped>.wstl-search{ display: inline-block; position: relative; .wstl-search-input{ display: inline-block; height: 100% } .wstl-search-panel{ position: absolute; border: 1px solid #e3e3e3; box-sizing: border-box; width: 100%; ul{ margin: 0; padding: 0; list-style-type: none; li { padding: 8px 10px; background: #fff; font-size: 14px; &:hover{ background: #e3e3e3; cursor: pointer; } } } }}</style>2、添加注册函数,加入该文件为index.js。//引用组件import Search from ‘./src/search’;//定义注册函数,当Vue.use(Search)时,会调用该函数,在该函数内可以注册组件,添加全局方法等等。Search.install = function(Vue) { //注册组件,组件名即Search.name Vue.component(Search.name, Search);};export default Search;3、Vue中注册组件import Vue from ‘vue’;//引入上面定义注册函数的index.js文件import Search from ‘index.js’;//注册组件库Vue.use(Search);经过这三步,就可以使用刚刚开发的那个组件了。如下:<wstl-search></wstl-search>这就是我们刚刚开发的组件。它的原理是什么呢?这是Vue提供的一个很方面的功能,当我们调用Vue.use(Search)的时候,Vue会调用Search.install方法,该方法第一个参数是Vue构造器,由于我们在Search.install方法里执行了如下代码:Vue.component(Search.name, Search);即我们调用Vue.use(Search)的时候,就注册了Search组件,组件名是wstl-search。这样我们就实现了插件的开发。Element UI主要就是利用这个特性开发的。UI库demo的搭建过程挺麻烦的,不一一讲解,我直接把我搭建好的demo地址发出来,需要的可以直接下载。git地址: https://github.com/xubaodian/… 简单解释下demo目录文件。packages文件夹是组件地址,packages/index.js是组件入口地址,所有组件都在install方法中注册到Vue实例中。config文件夹下是webpack配置,是我自己写的,参考Vue官方脚手架的webpack配置,有些区别,在文件中都有注释。webpack.dev.js:启动example的webpack配置,example用来测试组件webpack.prod.js:生成example的生产环境文件的webpack配置webpack.common.js:打包组件库的webpack配置,所有组件生成一个js文件和一个css文件webpack.component.js:分开打包组件的webpack配置,每个组件生成一个js文件和一个css文件src文件夹下是测试组件的Vue项目。有什么疑问可以在给我留言,或发邮件给我,472784995@qq.com,或在github上留言。 ...

January 5, 2019 · 2 min · jiezi

美团开源Graver框架:用“雕刻”诠释iOS端UI界面的高效渲染

Graver 是一款高效的 UI 渲染框架,它以更低的资源消耗来构建十分流畅的 UI 界面。Graver 独创性的采用了基于绘制的视觉元素分解方式来构建界面,得益于此,该框架能让 UI 渲染过程变得更加简单、灵活。目前,该框架已经在美团 App 的外卖频道、独立外卖 App 核心业务场景的大多数业务中进行了应用,同时也得到美团外卖内部技术团队的认可和肯定。App 渲染性能优化是一个普遍存在的问题,为了惠及更多的前端开发同学,美团外卖 iOS 开发团队将其进行开源,Github 项目地址与使用文档详见:https://github.com/Meituan-Di… 。我们希望该框架能够应用到更广阔的业务场景。当然,我们也知道该框架尚有待完善之处,也希望能与更多技术同行一起交流、探讨、共建。前言我们为什么需要关注界面的渲染性能?App 使用体验主要包含产品功能、交互视觉、前端性能,而使用体验的好与坏,直接影响用户持续使用还是转而使用其他 App,所以我们非常关注 App 的渲染性能。而且在互联网产品流量竞争愈发激烈的大背景下,优质的使用体验可以为现有用户提供更好的服务,进而提高用户转化和留存,这也意味着创收、盈利。<center><font color=gray size = 2>图1 使用体验与转化、留存</font></center>背景美团外卖 App 从2013年成立至今,已经走过了五个春秋,在技术层面先后经历了快速验证、模块化、精细化和平台化四个阶段,产品形态上也日趋成熟。在此期间,我们构建并完善了监控、报警、容灾、备份等各项基础设施,Metrics 即是其中的性能监控系统。曾经一段时间,我们以外卖 App 首页商家卡片列表为例,通过 Metrics 性能监控系统发现其在 FPS、CPU、Memory 等方面的各项指标并不理想。于是,通过 Xcode 自带的 TimeProfile 等性能检测工具,然后结合代码分析等手段找到了现存性能瓶颈。与此同时,我们梳理其近半年的迭代版本需求发现,UI 往往需要根据不同场景甚至不同用户展示不同的内容。为了不断迎合用户的需求,快速应对市场变化,这种特征还会持续存在。然而,它会带来以下问题:视图层级愈加复杂、视图数量愈加众多,从版本长期迭代来看是潜在的性能瓶颈点。如何快速、高效支撑 UI 变化,同时保证不会二次引入性能瓶颈。<center><font color=gray size = 2>图2 影响渲染性能、研发效率的瓶颈点</font></center>Graver 介绍为了解决现存的性能瓶颈以及后续潜在的性能瓶颈,我们期望构建一套解决方案,该方案能在充分满足外卖业务特征的前提下,以标准化、一站式的方式解决 iOS 端 App 的渲染性能问题,并且对研发效率有一定提升, Graver(雕工)框架应运而生。因为 Graver 独创性地采用了全新的视觉元素分解思路,所以该框架使用起来十分灵活、简单。我们先来看一下 Graver 的主要特点:性能表现优异以外卖 App 首页商家列表为例,应用 Graver 之后5分位滚动帧率从满帧的<font color=red size = 2>84%</font>提升至<font color=red size = 2>96%</font>,50分位几乎满帧;CPU 占用率下降了近<font color=red size = 2>6个百分点</font>,有效提升了空闲 CPU 的资源利用率,降低了峰值 CPU 的占用率。如图3所示:<center><font color=gray size = 2>图3 优化前后技术指标对比</font></center>“一站式”异步化Graver 从文本计算、样式排版渲染、图片解码,再到绘制,实现了全程异步化,并且是线程安全的。使用 Graver 可以一站式获得全部性能优化点,可以让我们:不再担心散点式的“遇见一处改一处”的麻烦。不再担心离屏渲染等各种可能导致性能瓶颈的问题,以及令人头痛的解决办法。不再担心优化会有遗漏、优化不到位。不再担心未来变化可能带来的任何性能瓶颈。性能消耗的“边际成本”几乎为零Graver 渲染整个过程除画板视图外完全没有使用 UIKit 控件,最终产出的结果是一张位图(Bitmap),视图层级、数量大幅降低。以外卖 App 首页铂金展位视图为例,原有方案由58个控件、12层级拼接而成;而应用 Graver 后仅需1个视图、1级层级绘制而成。 伴随着需求迭代、视觉元素变化,性能消耗恒属常数级。如图4所示:<center><font color=gray size = 2>图4 外卖 App 铂金展位应用 Graver 前后对比</font></center>渲染速度快Graver 并发进行多个画板视图的渲染、显示工作。得益于图文混排技术的应用,达到了内存占用低,渲染速度快的效果。由于排版数据是不变的,所以内部会进行缓存、复用,这又进一步促进了整体渲染效率。Graver 既做到了高效渲染,又保证了低时延页面加载。<center><font color=gray size = 2>图5 渲染效率说明</font></center>以“少”胜“繁”Graver 重新抽象封装 CoreText、CoreGraphic 等系统基础能力,通过少量系统标准图形绘制接口即可实现复杂界面展示。基于位图(Bitmap)的轻量事件交互系统如上述所说,界面展示从传统的视图树转变为一张位图,而位图不能响应、区分内部具体位置的点击事件。Graver 提供了基于位图的轻量事件交互系统,可以准确识别点击位置发生在位图的哪一块“绘制单元”内。该“绘制单元”可以理解为与我们一贯使用的某个具体 UI 控件相对应的视觉展示。使用 Graver 为某一视觉展示添加事件如同使用系统 UIButton 添加事件一样简单。全新的视觉元素分解思路Graver 一改界面编程思路,与传统的通过控件“拼接”、“添加”,视图排列组合方式构建界面不同,它提供了灵活、便捷的接口让我们以“视觉所见”的方式构建界面。这一特点在下文Graver使用中详细阐述,正是因为该特点实现了研发效率的提升。Graver 使用Graver 引入了全新的视觉元素分解的思路。借助该思路可以实现通过一种对象来表达任一视觉元素、甚至是任一视觉元素的组合,从而消除界面布局的复杂性。我们先来回顾下传统界面的构建方式,以外卖 App 商家卡片其中一种样式为例,如图6所示:<center><font color=gray size = 2>图6 外卖 App 商家卡片</font></center>在实现商家卡片的界面样式时,通常会根据视觉上的识别、交互要求来建立界面展示与系统提供的 UI 控件间的映射关系。以标号②位置的样式为例,在考虑复用的情况下通常这部分会使用三个系统控件来完成,分别是左侧蓝底的“预订”使用 UILabel 控件、右侧的蓝色边框“2.26.21:30起送”使用 UILabel 控件、把左右两侧 UILabel 控件装起来的 UIView 控件;在确定好采用的 UI 控件之后,需要针对展示样式分门别类的设置各个控件的渲染属性来实现图示 UI 效果,渲染属性通常一部分预设,一部分根据业务数据的不同再进行二次设置;其次,设置各个控件的内容属性实现业务数据内容的展示,展示的内容一般是网络业务数据经逻辑处理、加工后的数据。如果涉及到点击事件,还需要添加手势或者更换成 UIButton 控件。接下来,需要根据视觉要求实现排版逻辑,以标号⑧、⑨为例,当标号⑧位置的数据没有的情况下,需要上提标号⑨位置的“美团专送”到图示标号⑧位置。诸如类似的排版逻辑随处可见。对于图示任一位置的展示内容都存在上述的循环思考、编写工作。随着界面元素的增加、变化,问题会变得更加复杂。传统的界面构建方式其实是在 UI控件的维度去分解视觉元素,具体是做以下四方面的编写工作:控件选择:根据展示内容、样式、交互要求确定采用哪种系统控件。布局信息:UI 控件的大小、位置,即 Frame。内容信息:UI 控件展示出来的业务数据,如标号①位置的“星巴克咖啡店”。渲染信息:UI 控件展示出来的效果,如字体、字号、透明度、边框、颜色等。最后,将各个控件以排列组合方式合成为一棵视图树。Graver 框架提供了以画板视图为基础,通过对更底层的 CoreText、CoreGraphic 框架封装,以更贴近“视觉所见”的角度定义了全新视觉元素分解、界面展示构建的过程。通常“视觉所见”可划分为两部分:静态展示、动态展示。静态展示包含图片、文本;动态展示包含视频、动画等。在视觉展示全部为静态内容的时候,一个 Cell 即是一个画布,除此以外没有任何 UI 控件;否则,可以按需灵活的进行画布拆分来满足动画、视频等需要。<center><font color=gray size = 2>图7 画板和传统视图树</font></center>以图6商家卡片中标号②、⑧为例,新实现方式的伪代码是这样的:WMMutableAttributedItem *item = [[WMMutableAttributedItem alloc] init];[[[[item appendImage:[[UIImage wmg_imageWithColor:“blue”] wmg_drawText:“预订”]] appendImage:[[UIImage wmg_imageWithColor:“clear” borderWidth:1 borderColor:“blue”] wmg_drawText:“2.26.21:30起送”] appendWhiteSpaceWithWidth:“width”]//总体宽度减去②和⑧的宽度总和剩余部分 apendText:“50分钟|2.5km”];上述实现方式即是把标号②、⑧部分作为一个整体来实现,任何单一系统控件都无法做到这一点。Graver 渲染原理<center><font color=gray size = 2>图8 Graver 工作时序</font></center>如图8所示,Graver 涉及多个队列间的交互,以外卖 App 商家列表为例,整体流程如下:主线程构建请求参数,创建请求任务并放入网络线程队列中,发起网络请求。网络线程向后端服务发起请求,获得对应的业务模型数据(如包含了店铺名称,商家头图,评分,配送时长,客单价,优惠活动等店铺属性的商家卡片列表)。网络线程创建包含业务模型数据(如商家卡片列表)的排版任务,提交到预排版线程处理,进入预排版流程。预排版队列取出排版任务,交由布局引擎计算 UI 布局,将业务模型解析成可被渲染引擎直接处理的,包含布局、层级、渲染信息的排版模型。解析结束后,通知主线程排版完成。主线程获取排版模型后,随即触发内容显示。根据相对屏幕位置及出现的先后顺序,创建包含将需要显示区域信息的绘制任务,放入异步绘制线程队列中,发起绘制流程。异步绘制线程队列取出绘制任务,进行图文绘制,最终输出一张包含了图文内容(如商家卡片)的图片。绘制任务结束后,通知主线程队绘制完成,主线程随后展示绘制区域。整体按照队列间串行、队列内并行的方式执行。业务应用Graver 在外卖内部发布之后,我们也将其推广到更多的业务线,并希望 Graver 能够成为对业务开展有重要保障的一项基础服务。经过半年多的内部试用,Graver 的可靠性、渲染性能、业务适应能力也受到外卖内部的肯定和认可。截止发稿时,Graver 已经基本覆盖了美团 App 的外卖频道、独立外卖 App 核心业务场景的大多数业务。下面列举 Graver 在外卖业务的部分应用案例:经验总结总结一下,对于界面渲染性能优化而言,要站在一个更高角度来思考问题的解决方案。横向上,从普适性角度解决性能瓶颈点,避免其他人遇到类似问题的重复工作;纵向上,从长远考虑问题做到防微杜渐,一次优化,长期受益。基于此,我们提出一站式、标准化的渲染性能解决方案。诚然,这会遇到很多难点。面对界面样式构建的问题,系统 UIKit 框架着实为我们提供了便利,然而有时候我们需要跳出固有思维,尝试建立一套全新界面构建、视觉元素分解的思路。参考资料前端感官性能的衡量和优化实践作者简介洋洋,美团高级工程师。2018年加入美团,目前负责【美团外卖】和【美团外卖频道】的 iOS 客户端首页业务,以及支撑首页业务的技术架构、工具和系统的开发和维护工作。招聘美团外卖长期招聘 Android、iOS、FE 高级/资深工程师和技术专家,Base 北京、上海、成都,欢迎有兴趣的同学投递简历到chenhang03#meituan.com。 ...

December 24, 2018 · 1 min · jiezi

程序员练级攻略(2018):前端 UI/UX设计

这个是我订阅 陈皓老师在极客上的专栏《左耳听风》,我整理出来是为了自己方便学习,同时也分享给你们一起学习,当然如果有兴趣,可以去订阅,为了避免广告嫌疑,我这就不多说了!以下第一人称是指陈皓老师。前端还有一个很重要的事就是设计。作为前端人员,我们有必要了解现在的一些知名且流行的设计语言或是一些设计规范或是设计方法,学习它们的设计思想和方法,有助于我们拓宽眼界、与时俱进。我并不觉得这些内容是设计师要学习的,如果你要成为一个前端程序员,那么学习这些设计上的东西可以让你有更好的成长空间。对于学习设计的新手来说,推荐看看 7 steps to become a UI/UX designer ,这是一篇很不错的让新手入门的文章,非常具有指导性。首先,你得开始学习设计的一些原则和套路,如配色、平衡、排版、一致性等。还有用户体验的 4D 步骤——Discover、Define、Develop 和 Delivery。然后,开始到一些网站上找灵感。接下来,是到不同的网站上读各种文章和资源,开始学习使用设计工具,最后是找人拜师。此外,其中还链接了其它一些不错的文章、网站、博客和工具。我认为,这篇文章是一篇很不错的设计师从入门到精通的练级攻略。虽然有这么一个速成的教程,但我觉得还是应该系统地学习一下,所以有了下面这些推荐。图书和文章推荐先推荐几本书。Don’t Make Me Think ,这是我看的第一本和设计相关的书。这本书对我的影响也比较深远。这本书践行了自己的理论,整本书短小精悍,语言轻松诙谐,书中穿插大量色彩丰富的屏幕截图、趣味丛生的卡通插图以及包含大量信息的图表,使枯燥的设计原理变得平易近人。Simple and Usable Web,Mobile,and Interaction Design ,中文版译名为《简约至上》。本书作者贾尔斯(Giles)有 20 多年交互式设计的探索与实践。提出了合理删除、分层组织、适时隐藏和巧妙转移这四个达成简约至上的终极策略,讲述了为什么应该站在主流用户一边,以及如何从他们的真实需求和期望出发,简化设计,提升易用性。Designing with the Mind in Mind: Simple Guide to Understanding User Interface Design Rules ,中文版译名为《认知与设计:理解 UI 设计准则》。这本书语言清晰明了,将设计准则与其核心的认知学和感知科学高度统一起来,使得设计准则更容易在具体环境中得到应用。涵盖了交互计算机系统设计的方方面面,为交互系统设计提供了支持工程方法。不仅如此,这也是一本人类行为原理的入门书。Designing Interfaces: Patterns for Effective Interaction Design ,中文版译名为《界面设计模式》。这本书开篇即总结了"与人有关"的各类问题,为读者提供了界面设计总体思路上的指引,帮助读者举一反三。然后,收集并分析了很多常用的界面设计模式,帮助读者理解在实现级别的各种常用解决方案,将它们灵活地运用到自己的设计中。除了上面的这几本书,还有下面的这几篇文章也是很不错的,推荐一读。The Psychology Principles Every UI/UX Designer Needs to Know ,这篇文章讲述了 6 大用户界面用户体验设计的心理学原则。18 designers predict UI/UX trends for 2018The Evolution of UI/UX Designers Into Product Designers ,这篇文章是 Adobe 公司的一篇博客,其在回顾整个产品设计的演化过程中有一些不错的思考和想法,并提供了一些方法论。原子设计(Atomic Design)在 2013 年网页设计师布拉德·弗罗斯特(Brad Frost)从化学中受到启发:原子(Atoms)结合在一起,形成分子(Molecules),进一步结合形成生物体(Organisms)。布拉德将这个概念应用在界面设计中,我们的界面就是由一些基本的元素组成的。乔希·杜克(Josh Duck)的"HTML 元素周期表"完美阐述了我们所有的网站、App、企业内部网、hoobadyboops 等是如何由相同的 HTML 元素组成的。通过在大层面(页)和小层面(原子)同时思考界面,布拉德认为,可以利用原子设计建立一个适应组件的动态系统。为什么要玩原子设计,我认为,这对程序员来说是非常好理解的,因为这就是代码模块化重用化的体现。于是,你就是要像搭积木一样开发和设计网页,当你把其模块化组件化了,也更容易规范整体的风格,而且容易维护……这些都意味着你可以更容易地维护你的代码。所以,这个方法论导致了 Web 组件化的玩法。这是设计中非常重要的方法论。关于这个设计方法论,你可以阅读一下下面这几篇文章。Atomic Design 原子设计┃构建科学规范的设计系统网页设计:Atomic Design 简介及工作实例但是,真正权威的地方还是布拉德·弗罗斯特的电子书、博客和实验室,可以从中获取更多的信息。电子书:Atomic Design by Brad Frost 是布拉德·弗罗斯特写的一本书。博 客:Atomic Design 是布拉德·弗罗斯特的博客。实验室:Pattern lab 是布拉德·弗罗斯特依照这个设计系统所建立的一套工具,可以前往 Pattern Lab 的 GitHub 来试试 Atomic design。接下来是关于这个设计方法和 React.js 框架的几篇文章。Atomic Design with ReactAtomic Components: Managing Dynamic React Components using Atomic Design设计语言和设计系统下面来介绍一下设计语言和设计系统。Fluent Design SystemFluent Design System 中文翻译为流畅设计体系,是微软于 2017 年开发的设计语言。流畅设计是 Microsoft Design Language 2 的改版,其中包含为所有面向 Windows 10 设备和平台设计的软件中的设计和交互的指导原则。该体系基于五个关键元素:光感、深度、动效、材质和缩放。新的设计语言包括更多对动效、深度及半透明效果的使用。过渡到流畅设计体系是一个长期项目,没有具体的完成目标,但是从创作者更新以来,新设计语言的元素已被融入到个别应用程序中。它将在未来的 Windows 10 秋季创作者更新中更广泛地使用,但微软也表示,该设计体系不会在秋季创作者更新内完成。微软于 2017 年 5 月 11 日的 Microsoft Build 2017 开发者大会上公开了该设计体系。What’s new and coming for Windows UI: XAML and composition ,从概念上讲了一下 Fluent Design System 的各个部分。Introducing Fluent Design ,介绍了 Fluent Design System 的各个部分。还有 Build 2018 上的一些微软的 YouTube 分享。Fluent Design: Evolving our Design System : Build 2018Microsoft Build 2018 - Fluent Design System DemoMicrosoft Build 2018 - Fluent Design System EvolutionFluent Design System inside of Microsoft: Office : Build 2018Material DesignMaterial Design 中文翻译为质感设计,或是材质设计、材料设计。这是由 Google 开发的设计语言。扩展于 Google Now 的"卡片"设计,Material Design 基于网格的布局、响应动画与过渡、填充、深度效果(如光线和阴影)。设计师马蒂亚斯·杜阿尔特(Matías Duarte)解释说:“与真正的纸张不同,我们的数字材质可以智能地扩大和变形。材质具有实体的表面和边缘。接缝和阴影表明组件的含义。“Google 指出他们的新设计语言基于纸张和油墨。Material Design 于 2014 年的 Google I/O 大会上发布(参看 Google I/O 2014 - Material witness: How Android material applications work)。其可借助 v7 appcompat 库用于 Android 2.1 及以上版本,几乎支持所有 2009 年以后制造的 Android 设备。随后,Material Design 扩展到 Google 的网络和移动产品阵列,提供一致的跨平台和应用程序体验。Google 还为第三方开发人员发布了 API,开发人员可将质感设计应用到他们的应用程序中。除了到 官网 学习 Material Design,你还可以访问 Material Design 中文版 来学习。另外,Wikipedia 上有一张 Material Design 实现的比较表,供你参考。下面是几个可供你使用的 Material UI 的工程实现。Material Design Lite ,这是 Google 官方的框架,简单易用。Materialize ,一组类似于 Bootstrap 的前端 UI 框架。Material-UI 是基于 Google Material Design 的 React 组件实现。MUI 是一个轻量级的 CSS 框架,遵循 Google 的 Material Design 设计方针。其它公司接下来再来推荐其它几家公司的设计语言。苹果公司的设计指南,在这个网站有苹果的各种设备的设计规范和指导,一方面可以让你的 App 能和苹果的 UI 融合在一起,另一方面,你也可以从中看到苹果的审美和思维方式。IBM 公司的设计语言 ,我们总觉得 IBM 公司是一家比较传统的没有新意的公司,但是并不是这样的。IBM 公司的这个设计语言的确比较出众。所以,在这里推荐一下。Salesforce 公司的 Lightning Design System ,是在 Salesforce 生态系统中用于创建统一 UI 的设计模式、组件和指南的集合,是一个企业级的产品。Facebook Design - What’s on our mind? ,Facebook 的设计师们收集的一系列的文章、视频和资源。很不错哦。动画效果设计我认为,要了解 Web 动画效果设计的第一步,最好的地方是 CodePen。这个网站不只是让人分享 HTML、CSS 和 JavaScript 代码的网站。其中也有很多分享样例都和动画效果有关。这个网站可以让你对动画效果有一些感性认识,当然还有代码供你参考。接下来,我们要了解动画效果设计的一些方法。基本上来说,动画设计都会受 “动画的 12 项基本法则 " 的影响,这个方法论源自于迪士尼动画师奥利·约翰斯顿(Ollie Johnston)和弗兰克·托马斯(Frank Thomas)在 1981 年所出的《The Illusion of Life: Disney Animation》一书。这些法则已被普遍采用,至今仍与制作 3D 动画法则有关联。这里还有一篇文章 “Understand the 12 principles of animation” 是对这个法则的解读和理解。除此之外,还有几个动画设计指南和相关文章供你参考和学习。6 Animation Guidelines for UX Design。这是 Prototypr 公司的一个指南,其中主要指出,动画效果不是为了炫配,而是能让你的 UI/UX 能活起来,自然,不消耗时间,并且是生动故事型的动画效果。其中还推荐了如下几篇很不错的文章。Transitional InterfacesUI Animation and UX: A Not-So-Secret FriendshipInvisible animationCreating Usability with Motion: The UX in Motion ManifestoDesigning Interface Animation ,这篇文章同样说明,任何一个小动画都是要讲一个微故事的,而且这些微故事会和你的品牌和产品理念相融合。动画会给人更深的印象,让人们更容易记住你。这篇文章主要是讲品牌动画。Animation principles in motion design ,这篇文章有点像设计模式,给了一些动画效果的套路和演示。Creating Usability with Motion: The UX in Motion ManifestoIntegrating Animation into a Design SystemGreat UI/UX Animations 是设计师丹尼尔(Daniel)收集的一些很不错的动画,可以给你一些灵感。Great UI/UX Animations 第一组Great UI/UX Animations 第二组相关资源下面分享一下 UI/UX 设计的相关资源。文章资源主要有以下这些。文章资源Web Designer News ,一个文章聚合的网站。除此之外,还有两个文章聚合网站,你也可以订阅。一个是Designer News ,另一个是 Reddit Web Design。Marvel Blog ,Marvel 团队的博客。The Next Web ,内容主要涵盖国际技术新闻、商业和文化等多个方面。Medium - Design ,Medium 现在已经成为一个好文章的集散地了,这个地方必去。Smashing Magazine ,这个地方是给专业的 Web 设计师和程序员的。不但有设计还有 HTML、CSS 和 JavaScript 等各种资源。Sitepoint ,这个网站上也有很多不错的给 Web 前端程序员和设计师看的文章(当然,给程序员看的有点简单了,我觉得更像是让设计师来学写程序的网站)。设计收集接下来推荐一些优秀设计的聚集地。Awwwards ,这个网站给一些设计得不错网站的评分,在这里你可以看到很多设计不错的网站。One Page Love ,就是一个单页的网页设计的收集。Inspired UI ,移动 App 的设计模式。Behance,这个地言有很不错的很有创意的作品。Dribbble ,这应该是设计师都知道也都爱去的网站。除了你可以看到一些很不错的作品外,你还可以在这里看到很多不错的设计师。UI Movement ,也是个设计的收集网站,上面有很多很不错的 UI 设计,大量的动画。虽说会像抖音一样,让你不知不觉就看了好几小时,但是它比抖音让你的收获大多了。小结总结一下今天的内容。我并不认为 UI/UX 设计这些内容只是设计师要学习的,如果你要成为一个前端程序员,那么学习这些设计上的东西可以让你有更好的成长空间。首先,我推荐了一些图书和文章,让你更好地了解经典的设计原则和指导思想。然后介绍了原子设计,以及深入学习和理解这一设计方法论的图书、文章和其他相关资源。最后分享了当下主流和知名公司中在用的设计语言和设计系统,并给出了大量的学习资源,推荐了一些优秀设计的聚集地。相信通过学习这些内容,你在 UI/UX 设计方面不仅能收获方法,还能获得非常多的灵感。你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习! 时时的更新动态可以关注公众号《大迁世界》 ...

November 29, 2018 · 3 min · jiezi

ui2code中的深度学习+传统算法应用

背景在之前的文章中,我们已经提到过团队在UI自动化这方面的尝试,我们的目标是实现基于 单一图片到代码 的转换,在这个过程不可避免会遇到一个问题,就是为了从单一图片中提取出足够的有意义的结构信息,我们必须要拥有从图片中切割出想要区块(文字、按钮、商品图片等)的能力,而传统切割算法遇到复杂背景图片往往就捉襟见肘了(见下图),这个时候,我们就需要有能力把复杂前后景的图片划分为各个层级图层,再交给切割算法去处理,拿到我们期望的结构信息。经过传统切割算法处理,会无法获取图片结构信息,最终只会当成一张图片处理。在业界,图片前后景分离一直是个很麻烦的命题,业界目前比较普遍采用的解决方案是计算机视觉算法提取,或是引入人工智能来解决,但直到现在,都没有百分百完美的解决方案。那是否能引入AI来解决这个问题呢,我们来看一下,目前使用AI并拿到比较不错结果的解法是fcn+crf,基本上能够把目标物体的前景轮廓框出来,但缺点也很明显:准确率只有80%左右边缘切割无法达到像素级别打标成本非常大难以训练AI是个黑盒,结果不可控在考虑到使用AI伴随的问题之外,咱们也一起来思考下,难道AI真的是解决前后景分离的最佳解法吗?其实不是的,我们知道,一个页面,或者说设计稿,一个有意义的前景,是具有比较明显特征的,比如说:规则的形状:线段、矩形、圆形、圆角、是否对称等形状上是否有文字,或者说是类似于文字的信息是否闭合让我们一起来验证下这个思路的可行性。实践结果在尝试了非常的多计算机视觉算法之后,你会发现,没有一种算法是能够解决掉这个问题的,基本上是可能一种算法,在某种场景下是有效的,到了另外一个场景,就又失效了,而且就算是有效的场景,不同颜色复杂度下,所需要的最佳算法参数又是不相同的。如果case by case来解决的话,可以预期未来的工程会变得越来越冗杂且不好维护。那是不是可以这样呢,找到尽可能多的前景区域,加一层过滤器过滤掉前景可能性低的,再加一层层级分配器,对搜索到的全部前景进行前后层级划分,最后对图像进行修复,填补空白后景。咱们先来看看效果,以下查找前景的过程:为了避免有的前景被忽略(图片大部分是有多层的,前景里面还会嵌套前景),所以一个前景被检测到之后不会去隐藏它,导致会出现一个前景被多次检测到的情况,不过这块加一层层级分配算法就能解决了,最终得到出来的分离结果如下:逻辑概要文字处理OCR获取文字粗略位置来看看例子,以下左图是闲鱼首页,右图是基于OCR给出的文字位置信息对文字区域进行标记(图中白色部分),可以看到,大致上位置是准确的 但比较粗糙 无法精确到每个文字本身 而且同一行的不同文字片段 OCR会当成一行去处理。同时,也会有部分非文字的部分 也被当成文字,比如图中的banner文案:切割、CNN鉴别器对以上结果标注的位置进行切割,切割出尽可能小的单个文字区域,交给CNN判断,该文字是否是可编辑的文字,还是属于图片文案,后者将当作图片进行处理,以下是CNN代码:""" ui基础元素识别"""# TODO 加载模型with ui_sess.as_default(): with g2.as_default(): tf.global_variables_initializer().run() # Loads label file, strips off carriage return ui_label_lines = [line.rstrip() for line in tf.gfile.GFile(“AI_models/CNN/ui-elements-NN/tf_files/retrained_labels.txt”)] # Unpersists graph from file with tf.gfile.FastGFile(“AI_models/CNN/ui-elements-NN/tf_files/retrained_graph.pb”, ‘rb’) as f: ui_graph_def = tf.GraphDef() ui_graph_def.ParseFromString(f.read()) tf.import_graph_def(ui_graph_def, name=’’) # Feed the image_data as input to the graph and get first prediction ui_softmax_tensor = ui_sess.graph.get_tensor_by_name(‘final_result:0’)# TODO 调用模型with ui_sess.as_default(): with ui_sess.graph.as_default(): # UI原子级元素识别 def ui_classify(image_path): # Read the image_data image_data = tf.gfile.FastGFile(image_path, ‘rb’).read() predictions = ui_sess.run(ui_softmax_tensor, {‘DecodeJpeg/contents:0’: image_data}) # Sort to show labels of first prediction in order of confidence top_k = predictions[0].argsort()[-len(predictions[0]):][::-1] for node_id in top_k: human_string = ui_label_lines[node_id] score = predictions[0][node_id] print(’%s (score = %s)’ % (human_string, score)) return human_string, score文字抽离如果是纯色背景,文字区域很好抽离,但如果是复杂背景就比较麻烦了。举个例子:基于以上,我们能拿到准确的文本信息,我们逐一对各个文本信息做处理,文本的特征还是比较明显的,比如说含有多个角点,在尝试了多种算法:Harris角点检测、Canny边缘检测、SWT算法,KNN算法(把区域色块分成两部分)之后,发现KNN的效果是最好的。代码如下:Z = gray_region.reshape((-1,1)) Z = np.float32(Z)criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0) ret,label,center=cv2.kmeans(Z,K,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)center = np.uint8(center) res = center[label.flatten()]res2 = res.reshape((gray_region.shape))抽离后结果如下:查找前景强化图片边缘,弱化非边缘区域使用卷积核对原图进行卷积,该卷积核可以强化边缘,图像平滑区域会被隐藏。conv_kernel = [ [-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]卷积后,位与操作隐藏文字区域,结果如下:降噪对卷积后的图,加一层降噪处理,首先把图像转为灰度图,接着二值化,小于10像素值的噪点将被隐藏,最后使用cv2.connectedComponentsWithStats()算法消除小的噪点连通区域。基于文字位置,开始查找轮廓我们基于前面拿到的文字信息,选中文字左上角坐标,以这个点为种子点执行漫水填充算法,之后我们会得到一个区域,我们用cv2.findContours()来获取这个区域的外部轮廓,对轮廓进行鉴别,是否符合有效前景的特征,之后对区域取反,重新执行cv2.findContours()获取轮廓,并鉴别。判断内外部轮廓如果文字在轮廓内部,那拿到的区域将不会包含该区域的border边框,如果文字在轮廓外部,就能拿到包含边框的一整个有效区域(边框应该隶属于前景),所以咱们要判断文字和轮廓的位置关系(cv2.pointPolygonTest),如果在内部,会使轮廓往外扩散,知道拿到该轮廓的边框信息为止。前景鉴别器基于前面的步骤,我们会拿到非常多非常多的轮廓,其实绝大部分是无效轮廓以及重复检测到的轮廓,咱们需要加一层鉴别器来对这些轮廓进行过滤,来判断它是否是有效前景。定义有效shape我们会预先定义我们认为有意义的形状shape,比如说矩形、正方形、圆形,只要检测到的轮廓与这三个的相似度达到了设定的阀值要求,并且轮廓中还包含了文字信息,我们就认为这是一个有意义的前景,见代码:# TODO circlecircle = cv2.imread(os.getcwd()+’/fgbgIsolation/utils/shapes/circle.png’, 0), contours, _ = cv2.findContours(circle, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)self.circle = contours[0]# TODO squaresquare = cv2.imread(os.getcwd()+’/fgbgIsolation/utils/shapes/square.png’, 0), contours, _ = cv2.findContours(square, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)self.square = contours[0]# TODO rectrect = cv2.imread(os.getcwd()+’/fgbgIsolation/utils/shapes/rect.png’, 0), contours, _ = cv2.findContours(rect, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)self.rect = contours[0]匹配shape相似度多次尝试之后 发现score设置为3的效果是最好的。代码如下:# TODO 检测图形相似度def detect(self, cnt): shape = “unidentified” types = [self.square, self.rect, self.circle] names = [‘square’, ‘rect’, ‘circle’] for i in range(len(types)): type = types[i] score = cv2.matchShapes(type, cnt, 1, 0.0) # score越小越相似 # TODO 一般小于3是有意义的 if score<3: shape = names[i] break return shape, score单一匹配shape相似度的鲁棒性还是不够健壮,所以还引入了其他过滤逻辑,这里不展开。图像修复可以预见的,我们传入的图片只有一张,但我们划分图层之后,底层的图层肯定会出现“空白”区域,我们需要对这些区域进行修复。计算重叠区域需要修复的区域只在于重叠(重叠可以是多层的)的部分,其他部分我们不应该去修复。计算重叠区域的解决方案沿用了mask遮罩的思路,我们只需要计算当前层有效区域和当前层之上层有效区域的交集即可,使用cv2.bitwise_and# mask是当前层的mask layers_merge是集合了所有前景的集合 i代表当前层的层级数 # inpaint_mask 是要修复的区域遮罩# TODO 寻找重叠关系UPPER_level_mask = np.zeros(mask.shape, np.uint8) # 顶层的前景UPPER_level_mask = np.where(layers_merge>i, 255, 0)UPPER_level_mask = UPPER_level_mask.astype(np.uint8), contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 查找当前层的每个前景外轮廓overlaps_mask = np.zeros(mask.shape, np.uint8) # 当前层的所有前景的重叠区域for cnt in contours: cnt_mask = np.zeros(mask.shape, np.uint8) cv2.drawContours(cnt_mask, [cnt], 0, (255, 255, 255), cv2.FILLED, cv2.LINE_AA) overlap_mask = cv2.bitwise_and(inpaint_mask, cnt_mask, mask=UPPER_level_mask) overlaps_mask = cv2.bitwise_or(overlaps_mask, overlap_mask)# TODO 将当前层重叠区域的mask赋值给修复maskinpaint_mask = overlaps_mask修复使用修复算法cv2.INPAINT_TELEA,算法思路是:先处理待修复区域边缘上的像素点,然后层层向内推进,直到修复完所有的像素点。# img是要修复的图像 inpaint_mask是上面提到的遮罩 dst是修复好的图像dst = cv2.inpaint(img, inpaint_mask, 3, cv2.INPAINT_TELEA)延展本文大概介绍了通过计算机视觉为主,深度学习为辅的图片复杂前后景分离的解决方案,除了文中提到的部分,还有几层轮廓捕获的逻辑因为篇幅原因,未加展开,针对比较复杂的case,本方案已经能够很好的实现图层分离,但对于更加复杂的场景,比如边缘颜色复杂度高,噪点多,边缘轮廓不明显等更复杂的case,分离的精确度还有很大的提升空间。本文作者:闲鱼技术-云听阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

November 21, 2018 · 2 min · jiezi

从小数学就不及格的我,竟然用极坐标系表白了我的女神!(附代码)

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~本文由郭诗雅发表于云+社区专栏在数学中,极坐标系(英语:Polar coordinate system)是一个二维坐标系统。该坐标系统中任意位置可由一个夹角和一段相对原点—极点的距离来表示。在两点间的关系用夹角和距离很容易表示时,极坐标系便显得尤为有用;而在平面直角坐标系中,这样的关系就只能使用三角函数来表示。对于很多类型的曲线,极坐标方程是最简单的表达形式,甚至对于某些曲线来说,只有极坐标方程能够表示。(来自维基百科) 通过转换,极坐标的(, r)可以变换为直角坐标系中的(x,y)坐标,转化公式如下这样,许多用极坐标函数表示的曲线,都可以在js里面转成直角坐标系并画出来了。下面介绍一下以下几种曲线并用canvas绘制了曲线动画:1. 心形线函数:参数意义:a表示从x轴上从原点到最远点的一半。js动画:在js中实现时,只需将极坐标角度从0到360代入方程,求出(x,y)坐标2. 伯努利双纽线函数:参数意义:a表示从中心点到两端最远处的距离。js动画:绘制时,代入角度的区间是[-45,45],需要绘制(-x,-y)和(x,y)两部分的坐标。3. 星形线函数:参数意义:a表示从中心点到最远处的距离。js动画:在js中实现时,只需将角度从0到360代入方程,求解过程中不需要转换极坐标,直接代入x,y,求出(x,y)坐标4. 玫瑰线函数:参数意义:k代表有“几朵花瓣”,如果k是奇数,则得到的花瓣数就是k,如果k为偶数,则得到的花瓣数为2k。a同上表示从中心点到最远处的距离。js动画:当k为奇数时,角度区间在[0,180]即可闭合;当k为偶数时,区间在[0,360].5. 阿基米德螺线函数:参数意义:相邻“臂”之间的距离为2180ajs动画:角度一般要设置大于360,才有螺线效果,例子中角度为弧度,所以相邻“臂”之间的距离为2PIa。除此之外,还有这样以几何级数增大的螺线和从外往内描绘的螺线:6. 对数螺线函数:js动画:7. 双曲螺线函数:js动画:以上七种曲线的demo:展示地址总结 在简单的图形和动画轨迹上,我们可以换一种实现思维,例如通过函数来实现。最后,使用k=6的玫瑰线定义了(x,y)坐标,并设置z坐标为 (x,y)到z轴距离的3次方根,通过threejs,设置图片的顶点数,用曲线连接画了一个小demo。demo地址:代码地址展示地址问答游戏体系结构相关阅读玩转flex布局动感光波发射!Unity AR开发之 3d 物体识别小记Three.js 粒子系统学习小记:礼花效果实现 【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识

September 20, 2018 · 1 min · jiezi