首页

如何评价微信新上线的深色模式?

涛涛

微信在3.23号上线了传闻已久的 「暗黑版」,用来适配 iOS 的深色模式,相信不少同学已经安装并体验上了,如果还没安装的可以看看下面图例。

微信每有一次大动作都会引发全网性的讨论,而针对设计上的调整,往往只会迎来一片骂声。比如我们看看知乎中讨论的内容,感觉民愤都快压抑不住了。

但我们先别急着参与网上的声讨,现在站在设计师的角度,来评价一下迟到的微信深色模式。

深色和夜间模式傻傻分不清

很多人会把深色模式和夜间模式搞混,这里必须强调这是两种不同的模式,所以我们要对还没搞清楚状况的同学先做一个扫盲(最近扫盲好像搞的比较多…)。

先说夜间模式,是一个专门针对夜晚环境适配的设计版本。腾讯的 ISUX 团队之前做过调研,有 71.1% 的用户习惯在夜间不开灯看手机。

如果在深夜漆黑的房间中看手机屏幕,对我们的健康有非常大的损害,不仅表现在对视力的伤害上面,视网膜和神经元容易受损,同时主流的研究项目还表明会这会抑制我们褪黑素的分泌造成失眠。

所以,夜间模式,是一个用来降低屏幕对用户伤害,提升夜间使用体验的特殊模式。

通常,夜间模式会采取 降低尼特值、减少对比度、降低色彩明度、增加深色遮罩的方法,比如之前 QQ 官方的夜间模式示意图,大家感受一下,是不是有内味儿了?

这里提一下尼特这个概念,尼特是用来说明亮度的术语,1nit=1坎德拉/平方米。是现在各大手机发布会中介绍屏幕的时候都要强调的参数之一,因为尼特值越高,证明在户外大白天的环境中屏幕内容可以越清晰,而夜间模式做的就是用来降低显示亮度尼特值的。

然后再解释一下苹果的深色模式,苹果的 DarkMode,并不是一个专门面向深夜环境的模式。官方对此版本的解释,详见我们翻译的 iOS 官方文档中 112 页。

这是一个面向全天候的视觉风格,同时通过深色背景的对比,来加凸显图片、文字内容。包括上面那种官方的配图,大家应该就能感觉到主体元素比白色模式下更凸出,更激烈。

所以了解了这两个模式的区别,我们才能好好展开对微信深色模式的讨论。

微信深色模式专业总结

接下来,我们先来总结一遍微信的深色模式。首先是分析一遍它使用的背景色。

背景色纯灰色,有3个等级的灰度。熟悉我的都了解,看色彩的奥秘,靠16进制代码和 RGB 那是分析不出个什么所以然的。如果我们把它们转化成 HSB 一看,规律性就来了。

背景色从深到浅色的灰度值 B 值分为 10、14、18,是不是朗朗上口。应用的层级虽然和官方规范一样使用了三级,但是设置和官方的不同。

然后再看看其中使用的其它配色,其中主色保持了不变,其它应用到图标色彩,都进行了明度的调整,比如下图案例。

再看看文字的用色,也是纯灰色,标题使用 B:85,正文使用 B:65,注释使用 B:35(主要用色)。

而官方使用的文字色彩中,却并不是纯灰色,除了第一级的白色以外,其它灰阶的文字是由带有蓝色色相的色彩通过降低透明度来呈现的。

对中性色增加蓝色色值是一个常规操作,可以让这种灰色看起来有一点活力,不会像纯灰色那么单调,不过这次微信明显在文字的应用上更保守,一点色相也没有给。

为什么微信色彩另辟蹊径

这次微信被大面积吐槽的,就是颜色的应用上和官方的规范不一致,作为从业人员直接开喷是相当不专业的(最起码要先走个形式),这就是我要分析的重点了。

要说微信的 UED 团队,专业性肯定是国内最顶尖的,你们网上所有看过有关交互的方法论、可用性测试的分享, 他们肯定都有做过,而且执行得更专业。

直接用官方规范的黑底白字模式,微信团队不可能没有出过这样的方案。但很明显,这个方案最后被毙了,上了我们看见的这个版本,虽然不知道以后会不会变。

再看看下面官方发的一条微博。

其中已经提及了,是和苹果 「共同探索」 出来的方案,这是非常耐人寻味的。也就是说,这个不用官方的模式是苹果团队也通过的。

那么为什么要做的不一样呢?没有内幕消息,就根据自己的经验来判断一下。

我自己认为的一个非常重要的原因,就是对于 「夜间模式「 的兼容。前面不是讲暗黑模式和夜间模式不一样嘛?为什么微信的暗黑模式又去兼容夜间模式了。

这里面有几个小彩蛋,首先就是官方对深色模式的态度,在我看来越来越暧昧了。比如在显示与亮度设置页面里,有一个自动设置外观 —— 日出前保持深色外观的功能。这样,就等于默认将深色模式和夜间模式挂钩。

还有,如果过去我们没有整理 iOS13 的翻译,就不会发现,上面我们展示的那句专注于内容的解释,现在在官网已经被删掉了(你品,你细品)。

再说关于用户认知的问题上面,在 UI 群体中,能了解暗黑模式和夜间模式是不一样的可能就只占 10 分之一,那么对于普通用户来说,这个情况就更不乐观,能有 1% 的用户了解这个概念就不错了。所以,绝大多数用户会直接认为暗黑模式就是夜间模式。

如果暗黑模式直接当成夜间模式用,在深夜的环境里,观感会特别差,因为 —— 明暗对比度过高。

如果在黑底中直接用白字,那么可以说屏幕的文字和背景的对比度就是 100(HSB中 B 的差值),在一个漆黑的环境中会非常应验 「让内容脱颖而出」 的效果,刺激性会非常强烈,文字将变得非常尖锐,比如 QQ 暗黑模式,大家可以在被窝里打开下面这张图感受一下。

新的深色模式版本中,文字和背景的对比度基本控制在 50 左右,降低了一半。

并且,中英文字形在正负形上的差异(简单理解就是中文笔划更复杂),这个感觉会更强烈。比如我们用一个公众号页面举例,使用纯黑底白字,采用相同字号的中英文,看看显示的效果。

还有,纯黑背景色和白色的对比度,会根据屏幕的类型和参数不同而有不一样的感受,比如苹果从 X 后旗舰机型使用 OLED 屏幕,纯黑色区域将不会发光,进一步扩大对比度,使得文字变得更尖锐,更让人难以接受。

如果不是使用 OLED 屏幕的同学光看一个案例可能很难受,所以我们用纯黑的案例来对比一下现在的状态。

是不是发现明显在夜间的情况下黑白模式并不如另一个模式看起来舒适?而这种不舒适的差别并不会随着屏幕亮度降低而变化。

所以色彩并没有符合官方的原因,我的判断就在大环境中无法割裂夜间和深色模式的区别,同时也要让深色模式更适应夜间环境,做出了调整。而又因为它不是真正的夜间模式,所以对比度也不能像 QQ 之前的夜间版本一样将整体环境完全压暗。

你看,真是一个让人矛盾的过程……

为什么深色模式姗姗来迟

最后再简单讨论一个问题,为什么深色模式来得这么晚。很多用户一直嘲讽,不就是换一套皮肤的事嘛,为什么就是不上线。

外行可以看热闹,但是如果是从业人员就应该知道,微信这种体量的应用,上线深色模式绝对不是一个非常容易的事情。

适配黑暗模式首先需要使用苹果新的 iOS 13 SDK(开发者工具)进行编译,等于应用中有大量的代码需要调整,而这种升级调整的结果还会导致沉重的测试压力。有经历过 Darkmode 开发的团队应该都知道这绝对不是改改颜色就能上线的皮肤。

再看到知乎另一个回答中提到的:

另一方面点大家随便听听。使用 iOS 13 SDK 之后,Apple 要求 VoIP 推送必须使用 CallKit,否则应用程序会被终止。而由于众所周知的原因,CallKit 在中国大陆是无法使用的,这样的改动会降低微信语音电话的体验。

原文地址:https://www.zhihu.com/question/378027349/answer/1069072154

再者,抛去大量用户体验调研相关的工作,微信整个生态对于暗黑模式的不友好可以说是无解的。比如说公众号,有大量公众号内部的标题、分割线、引用语句是用图片做上去的,而图片还用的是白底(透明底黑字的也有),于是现在就产生了灾难性的阅读体验。

比如我的公众号:超人的电话亭,其中文章展示的截图。

而且因为公众号发出去是不能修改的,只能删除,那么这部分存量文章将无法更改,体验也无法扭转。而且公众号还支持文字色彩等自定义,那么你在白色背景下添加的颜色,可不会直接适配深色模式,尤其是官方也不可能轻易直接给你们 「适配」 掉。

而在夜间模式,正常访问的文章网页,也和公众号会很像,但是打开以后是白色背景的话,统一的体验在哪里?

再者还有小程序,小程序虽然也可以通过微信官方提供小程序的深色模式适配文档,对应的 SDK,但是小程序不是 APP,其中有大量小程序开发后是缺少维护的。

因为线下门店通过外包方做好一个小程序上线以后,没特殊的原因不会直接去更新它,那么这部分小程序的升级适配无从谈起,会出现打开小程序一个白一个黑的窘境。

最后,再讲一个微信里最高频使用的功能 —— 发表情。深色模式直接造成大量自定义表情报废,无法正常显示的问题,比如看看下面我自己发的表情。

前面提到的,都是不能解决的问题,这就是做深色模式的挑战,因为用户 UGC 内容是不可控的,官方不可能通过算法直接帮用户强行 「适配」。

而这些,就是做深色版的难点。

总结

以上总结内容多数为主观分析,纯粹站在 UI 设计师角度进行专业解读,不带入个人立场。而一定要我自己评价的话,那就是 :赶紧把这模式给我移除!!

再顺便提一点小感想,一个有数亿用户的产品,每一个小调整分量都不轻,都要慎之又慎。同时,你做的每一个决策,都意味着要站在其中一部分用户的对立面,因为你满足不了所有用户的需求。所以,这就是设计师的压力与挑战。

文章来源:优设    作者:超人的电话亭

你不知道的--save-dev和--save的区别

seo达人

网上对于这两个的区别解释都是统一口径的,一个是开发依赖,一个是线上依赖,打包发布需要用到的要添加到线上依赖,一模一样的回答,误导了很多人。今天自己测试一下这两个命令,记录一下。



–save-dev,会在devDependencies里面添加依赖



-D,会在devDependencies里面添加依赖



–save,会在dependencies里面添加依赖



-S,会在dependencies里面添加依赖



devDependencies和dependencies可以同时存在同一个包的依赖。



如果npm install xxx后面没有输入要保存到哪个里面,devDependencies和dependencies都没有。



我这边直接npm install jquery,node_modules下有jQuery。然后我删除node_modules,执行npm install,node_modules下并没有下载jQuery。



所以,安装依赖的时候如果没有加上要依赖到开发还是线上,只是临时的在node_modules里面帮你下载,而devDependencies和dependencies的依赖都会帮你下载。



然后我在devDependencies下安装依赖:



"devDependencies": {  

    "html-webpack-plugin": "^4.0.3", 

    "jquery": "^3.4.1",  

    "webpack": "^4.42.1", 

    "webpack-cli": "^3.3.11"

}



在入口文件引用和打印jQuery:



import $ from 'jquery'

console.log($)



打包之后,可以使用jQuery。



然后我在dependencies下安装依赖:



"dependencies": { 

    "html-webpack-plugin": "^4.0.3", 

    "jquery": "^3.4.1", 

    "webpack": "^4.42.1", 

    "webpack-cli": "^3.3.11"

}



在入口文件引用和打印jQuery:



import $ from 'jquery'

console.log($)



打包之后,可以使用jQuery。



测试的结果就是,无论是–save还是–save-dev,对于打包都没有任何影响。devDependencies和dependencies两种情况,打包出来的main.js都把jQuery打包进去。这两种情况,如果都没有引用jQuery的情况下,也都不会把jQuery打包。



接着在一个空白的项目里面下载axios,npm install axios -S,打开node_modules文件夹:







发现多出了另外三个依赖,查看axios下的package.json:



"dependencies": {



    "follow-redirects": "1.5.10"



}



查看follow-redirects下的package.json:



"dependencies": {



    "debug": "=3.1.0"



}



查看debugs下的package.json:



"dependencies": {



    "ms": "2.0.0"



}



最后ms的package.json没有dependencies。



而这几个包的devDependencies依赖的包没有一个下载。



接着我在node_modules把follow-redirects、debugs、ms都删了,把axios里面的package.js的dependencies给删了,然后执行npm install,发现没有下载follow-redirects、debugs、ms这几个,也证明了如果node_modules里面有下载的包,是不会重新去下载的。我把node_modules删除,执行npm install,这几个包又都下载下来了。



最后得出 的结论是,–save-dev和–save在平时开发的时候,对于打包部署上线是没有任何影响的。如果你是发布一个包给别人用,而你开发的包依赖第三方的包,那么你如果是–save,那么别人安装你开发的包,会默认下载你依赖的包,如果你是–save-dev,那么别人安装你开发的包,是不会默认帮忙下载你依赖的包。



其实发布的包如果没有必要,很少会默认帮你下载,比如bootstrap,依赖jQuery,怕你原本就下载了引起冲突,也不会在dependencies里面安装jQuery而是:



"peerDependencies": {



    "jquery": "1.9.1 - 3",



    "popper.js": "^1.16.0"



}



表示bootstrap依赖于这两个包,你必须安装,版本不固定,但是一定要安装这两个包,安装的时候会有警告:



peerDependencies WARNING bootstrap@ requires a peer of jquery@1.9.1 - 3 but none was installed



peerDependencies WARNING bootstrap@
requires a peer of popper.js@^1.16.0 but none was installed



当你引用了然后打包,报错:



ERROR in ./node_modules/_bootstrap@4.4.1@bootstrap/dist/js/bootstrap.js



Module not found: Error: Can't resolve 'jquery' in 'C:\Users\wade\Desktop\savedev\node_modules_bootstrap@4.4.1@bootstrap\dist\js'



 @ ./node_modules/_bootstrap@4.4.1@bootstrap/dist/js/bootstrap.js 7:82-99



 @ ./src/index.js



 



ERROR in ./node_modules/_bootstrap@4.4.1@bootstrap/dist/js/bootstrap.js



Module not found: Error: Can't resolve 'popper.js' in 'C:\Users\wade\Desktop\savedev\node_modules_bootstrap@4.4.1@bootstrap\dist\js'



 @ ./node_modules/_bootstrap@4.4.1@bootstrap/dist/js/bootstrap.js 7:101-121



 @ ./src/index.js



以上就是对–save和–save-dev的一些测试,想更快的得出结论其实是自己发布一个包。至于本人的答案是不是存在错误,欢迎指出,因为只是自己简单测试的结果。


视觉设计师与用户体验地图

前端达人

点击查看原图


用户体验地图(Customer Journey Map)是什么?

用户体验地图是从用户的视角出发,去理解用户、产品或者服务交互的一个重要的设计工具。

也可以说是以可视化的形式,来表现一个用户使用产品或者接受服务的体验情况,从体验的过程中来发现用户在整个体验过程中的问题点与情绪点,以此来从中提取出产品的优化点,方便对产品进行迭代,从而保证良好的用户体验。

经典案例

Chris Risdon绘制的欧洲铁路购票的体验地图

点击查看原图



上图中是欧洲铁路公司整个体验地图的一部分。欧洲铁路公司是一家美国经销商,为北美旅客提供一个独立预订火车票去欧洲各地的平台,而无需用户去网站预定。他们已经拥有了一个良好体验的网站和一个屡获殊荣的咨询中心,但他们希望通过所有接触点来优化用户使用过程,这样可以让他们更全面地了解,他们应该专注的投资,设计和技术资源。整体的“诊断”评价系统,包含一系列的重点举措,体验地图只是其中派生的一部分。体验地图帮助建立同理心图,来理解随着时间和空间的推移,用户与欧洲铁路公司服务系统交互时接触点的变化。

在这张体验地图中采用了五个关键组成一个体验地图,一个体验地图可以直观的表示用户操作流、期望、特定的目标、用户情绪状态和整体的体验点,做到整体把控和评估产品体验。

作用 :

点击查看原图



用户体验地图能帮助我们创造出一个有大局观的用户体验,更好的帮助我们理解用户的痛点和需求,帮助Team达成共识,非常有利于跨团队合作。

用户体验地图包含的内容 :

01b5465c17ca39a8012092526f5b27.jpg



其中包括,人群(产品的用户是哪一类人)、 用户的需求(用户想得到什么)、 路径(在某特定的场景下体验的整体过程) 、接触点  (产品与人或人与服务接触的关键点)、行为(用户的行为是什么样的?)、情绪  (体验过程中的感受心情) 、机会点  (过程中可以突破的点,可以成为特色的地方)、 解决方案  (解决用户在体验过程的痛点)、 问题  (解决用户在体验过程的痛点)。

用户画像 :

01c6935c17ca72a80121ab5d78d1c9.jpg


015b475c17ca8aa80120925274b13f.jpg


在准备开始绘制用户体验地图的时候,我们应该要确立用户群体 / 确定产品目标 / 了解用户目标,并作出用户画像。

视觉设计师怎么使用

举例(一):

01e1835c17cabfa80121ab5d29eade.jpg



那我们看看作为一名视觉设计师应该关注哪部分的流程。

视觉设计师的用户体验地图 :

01e7375c17cae4a801209252612bf2.jpg


我们的聚焦点应在上图的这几个部分。

0147a05c17caf9a80121ab5d287c70.jpg


所以当绘制完用户体验地图后,应该再绘制一份视觉设计师看的版本,我们设计师主要关注的视觉的触点。

定量方法(产品方向):

我们在行为和情绪上一般会使用问卷法、后台数据分析法;而在需求和问题上一般会使用焦点小组、访谈法、观察法、日志法和田野调查,下面就为大家来解释下这些方法。

焦点小组:是指从研究产品中所确定的全部用户群(总体)中抽取一定数量的用户来组成样本,根据样本信息推断用户群总体特征的一种调查方法。

访谈法:访谈,就是以口头形式向用户进行询问,根据被询用户的答复搜集客观的、不带偏见的事实信息,以准确地说明样本所要代表的总体的一种方式。

观察法:观察法是指研究者根据一定的研究目的、研究提纲或观察表,用自己的感官和辅助工具去直接观察用户,从而获得资料的一种方法。

日志法:是由用研人员按时间顺序,详细记录自己在一段时间内使用产品的过程,经过归纳、分析,达到分析产品目的的一种工作分析方法。

田野调查:在日常生活中,在一个有一个严格定义的空间和时间的范围内,体验特定用户群的日常生活与思想境界,通过记录自己的生活的方方面面,来展示不同阶段用户群的基本需求。

注意事项(5要点)

1. 在制作地图前,应理清楚产品的前期规划和需求,并且与同事达成共识。

2. 避免以自己的经验或者认知来确定用户体验地图中的接触点,应当真正的从用户的行为中去提取。

3. 不要将一些落后的信息加入到用户体验地图中。

4. 最好先在Team内部脑暴一份地图,再去与所制作的地图进行对比。

5. 用户体验地图不会涉及到实现方案和现实机制,只涉及用户的体验。

团队人员的合理搭配 :

将公司或者团队的PM、RD、运营、Leader等过来,详细的描述这一份用户体验地图,聆听他们的反馈。

在分析用户问题上 :

分为四个等级:ABCD,在对优先级进行排列的同时应该,考虑到产品在每个阶段的侧重点,根据不同的进度和情况,来对优先级进行排列,帮助我们整理问题和提炼最核心的一些体验问题,区分问题还能帮助我们更好的把握产品的优化方向。

视觉设计师应该关注的点 :

01c9015c17cbf7a801209252f6af88.jpg



视觉设计师的任务是什么?是有效的传达出产品的信息、简洁并且优雅的传达、通过视觉设计制造出愉悦的用户体验。用户在很多的场景下都可能接触到企业的产品或者是服务,这个服务接触带给用户的感受更多是偏向于视觉感知方面的。所以我们需要尽可能的列举出企业的产品或者服务与用户可能产生接触的场景、服务触点,再根据服务触点延伸出相关的“视觉触点”,用来梳理出我们需要输出的视觉产物,做出相对应的查漏补缺和优化,输出指导企业的品牌建设工作。而用户体验地图就很适合作为这样的工具。

“体验地图”对于优化视觉体验的意义 :

01fea65c17cc21a80121ab5d431aa7.jpg


整体性:系统性地规划品牌的视觉统一化工作,提升品牌建设工作的全面性和完整度。也可以避免未来工作中不同的品牌 / UI / 运营设计师对于品牌概念的理解不同而带来的设计出入。

01d6455c17cc3ba80121ab5d399fc3.jpg


品牌设计,是用户对于公司产品的直接印象,所以在品牌设计的要求就是:建立特征、保持特征、推广特征、美化特征、对于以上的要求,来提供完整且匹配的设计方案。

运营设计,运营设计的目标就是让用户尽可能的感知到产品的好,把产品的特点通过设计包装传递给用户,一个好的运营设计,应该是在用户看到你的设计作品后,会产生足够好的兴趣和好感,并愿意去关注你的产品。

UI设计,这是产品与用户接触过程中,频率最高、最直观的部分,目的是为了让用户认识到产品的相貌和气质,UI设计需要注意界面视觉层次的强弱、信息划分、用户的视线轨迹、色彩的表达、质感、舒适度等,来让用户觉得这个产品设计真好。

例如 :

OFO,以年轻人为主的共享骑行产品,无论是在品牌/运营/UI的设计上,都能让人感觉时尚、年轻、阳光、且有亲和力。

品牌设计 :


0151145c17cc66a80121ab5ddb966a.jpg

UI设计 :

01386c5c17cc9da80121ab5dd27af4.jpg


运营设计 :

01c5c35c17ccbea8012092520ea55d.jpg

UI设计 :

0105375c17cd2aa801209252066123.jpg


运营设计 :

0105375c17cd2aa801209252066123.jpg

设计师的进阶 :

01ff0e5c17cd5aa801209252db7ebb.jpg

在一开始的初级设计师阶段(也就是1.0阶段),我们需要从交互设计师手中接过交互设计稿,来对它进行气质进行改造,做出独特的视觉设计,也就是将其翻译为高保真稿,然后再与开发同学进行对接,也要保持视觉走查,以防实际效果与预期效果的不符;在这个1.0阶段我们的表现力和创造力,是最为主要的,如何去做出差异化?这是这个阶段的设计师需要考虑的,在这个APP设计趋同的大浪潮下,你如果能够做出不一样的设计,那么你则可以一鸣惊人,从众多水平相当的设计师中脱颖而出,这时你便可以考虑进入下一个阶段,也就是2.0。

0172935c17cd76a80121ab5d42695b.jpg

在高级设计阶段(即2.0阶段),这时候你就需要拥有更好的产品思维和逻辑能力,不仅仅只是从交互设计师拿到交互设计稿,直接上手开做,在这之前,你需要开始了解产品的业务定位、用户人群、产品目标、当前的问题、未来的迭代等,需求方这时候就成你的主要对接对象,需要你具备拆解需求、采集用户的需求、扩展业务、能进行设计验证的能力,能将产品的气质和品牌贯穿于整个产品(UI/运营/品牌),设计是怎么推导的,现在就不是仅仅只在停留在好看的层面上了,毕竟设计师不是画师,而是解决问题的,我们在做了某个设计后,就要去关注它的变化了,看看用户的反馈、商业转化率等等,这都是为你的下一次设计迭代做的参考。



站酷

分享到脉脉


转自:脉脉

原文链接:https://maimai.cn/article/detail?fid=988630001&efid=N-uHKNnf7vXGBmaFd3lZHA&use_rn=1

vue实现移动端悬浮窗效果

前端达人

本文讲述,在使用VUE的移动端实现类似于iPhone的悬浮窗的效果。

相关知识点

touchstart 当在屏幕上按下手指时触发

touchmove 当在屏幕上移动手指时触发

touchend 当在屏幕上抬起手指时触发
mousedown mousemove mouseup对应的是PC端的事件

touchcancel 当一些更高级别的事件发生的时候(如电话接入或者弹出信息)会取消当前的touch操作,即触发touchcancel。一般会在touchcancel时暂停游戏、存档等操作。

效果图

实现步骤

1.html

总结了一下评论,好像发现大家都碰到了滑动的问题。就在这里提醒一下吧。可将该悬浮 DIV 同你的 scroller web 同级。 —- (log: 2018-08-21)

html结构: <template> <div>你的web页面</div> <div>悬浮DIV</div> </template>

<template>
 <div id="webId">
 ...
 <div>你的web页面</div>
 <!-- 如果碰到滑动问题,1.1 请检查这里是否属于同一点。 -->
 <!-- 悬浮的HTML -->
 <div v-if="!isShow" class="xuanfu" id="moveDiv"
  @mousedown="down" @touchstart="down"
  @mousemove="move" @touchmove="move"
  @mouseup="end" @touchend="end"
 >
  <div class="yuanqiu">
  {{pageInfo.totalPage}}
  </div>
 </div>
 ...
 </div>
</template>

2.JS

<script>
data() {
 return {
 flags: false,
 position: { x: 0, y: 0 },
 nx: '', ny: '', dx: '', dy: '', xPum: '', yPum: '',
 }
}

methods: {
 // 实现移动端拖拽
 down(){
 this.flags = true;
 var touch;
 if(event.touches){
  touch = event.touches[0];
 }else {
  touch = event;
 }
 this.position.x = touch.clientX;
 this.position.y = touch.clientY;
 this.dx = moveDiv.offsetLeft;
 this.dy = moveDiv.offsetTop;
 },
 move(){
 if(this.flags){
  var touch ;
  if(event.touches){
   touch = event.touches[0];
  }else {
   touch = event;
  }
  this.nx = touch.clientX - this.position.x;
  this.ny = touch.clientY - this.position.y;
  this.xPum = this.dx+this.nx;
  this.yPum = this.dy+this.ny;
  moveDiv.style.left = this.xPum+"px";
  moveDiv.style.top = this.yPum +"px";
  //阻止页面的滑动默认事件;如果碰到滑动问题,1.2 请注意是否获取到 touchmove
  document.addEventListener("touchmove",function(){
   event.preventDefault();
  },false);
 }
 },
//鼠标释放时候的函数
 end(){
 this.flags = false;
 },
}
</script>

3.CSS

<style>
 .xuanfu {
 height: 4.5rem;
 width: 4.5rem;
 /* 如果碰到滑动问题,1.3 请检查 z-index。z-index需比web大一级*/
 z-index: 999;
 position: fixed;
 top: 4.2rem;
 right: 3.2rem;
 border-radius: 0.8rem;
 background-color: rgba(0, 0, 0, 0.55);
 }
 .yuanqiu {
 height: 2.7rem;
 width: 2.7rem;
 border: 0.3rem solid rgba(140, 136, 136, 0.5);
 margin: 0.65rem auto;
 color: #000000;
 font-size: 1.6rem;
 line-height: 2.7rem;
 text-align: center;
 border-radius: 100%;
 background-color: #ffffff;
 }
</style>

实现好JS逻辑,基本上,问题不大。

本文链接 http://www.luyixian.cn/javascript_show_166242.aspx



再加一点

css之display:inline-block布局

1.解释一下display的几个常用的属性值,inline , block, inline-block

  • inline(行内元素):
    1. 使元素变成行内元素,拥有行内元素的特性,即可以与其他行内元素共享一行,不会独占一行. 
    2. 不能更改元素的height,width的值,大小由内容撑开. 
    3. 可以使用padding上下左右都有效,margin只有left和right产生边距效果,但是top和bottom就不行.
  • block(块级元素):
    1. 使元素变成块级元素,独占一行,在不设置自己的宽度的情况下,块级元素会默认填满父级元素的宽度. 
    2. 能够改变元素的height,width的值. 
    3. 可以设置padding,margin的各个属性值,top,left,bottom,right都能够产生边距效果.
  •  inline-block(融合行内于块级):
    1. 结合了inline与block的一些特点,结合了上述inline的第1个特点和block的第2,3个特点.
    2. 用通俗的话讲,就是不独占一行的块级元素。如图:

图一:1.png

图二:

2.png

两个图可以看出,display:inline-block后块级元素能够在同一行显示,有人这说不就像浮动一样吗。没错,display:inline-block的效果几乎和浮动一样,但也有不同,接下来讲一下inline-block和浮动的比较。

 

2.inline-block布局 vs 浮动布局

    a.不同之处:对元素设置display:inline-block ,元素不会脱离文本流,而float就会使得元素脱离文本流,且还有父元素高度坍塌的效果

    b.相同之处:能在某程度上达到一样的效果

我们先来看看这两种布局:
图一:display:inline-block3.png

图二:4.png

对两个孩子使用float:left,我在上一篇浮动布局讲过,这是父元素会高度坍塌,所以要闭合浮动,对box使用overflow:hidden,效果如下:

>>乍一看两个都能做到几乎相同的效果,(仔细看看display:inline-block中有间隙问题,这个留到下面再讲)

c.浮动布局不太好的地方:参差不齐的现象,我们看一个效果:
图三:

图四:

>>从图3,4可以看出浮动的局限性在于,若要元素排满一行,换行后还要整齐排列,就要子元素的高度一致才行,不然就会出现图三的效果,而inline-block就不会。

 

3.inline-block存在的小问题:

a.上面可以看到用了display:inline-block后,存在间隙问题,间隙为4像素,这个问题产生的原因是换行引起的,因为我们写标签时通常会在标签结束符后顺手打个回车,而回车会产生回车符,回车符相当于空白符,通常情况下,多个连续的空白符会合并成一个空白符,而产生“空白间隙”的真正原因就是这个让我们并不怎么注意的空白符。

 

b.去除空隙的方法:
1.对父元素添加,{font-size:0},即将字体大小设为0,那么那个空白符也变成0px,从而消除空隙
现在这种方法已经可以兼容各种浏览器,以前chrome浏览器是不兼容的
图一:

 

c.浏览器兼容性:ie6/7是不兼容 display:inline-block的所以要额外处理一下:
在ie6/7下:
对于行内元素直接使用{dislplay:inline-block;}5.png
对于块级元素:需添加{display:inline;zoom:1;}

 6.png

4.总结:

display:inline-block的布局方式和浮动的布局方式,究竟使用哪个,我觉得应该根据实际情况来决定的:
a.对于横向排列东西来说,我更倾向与使用inline-block来布局,因为这样清晰,也不用再像浮动那样清除浮动,害怕布局混乱等等。
b.对于浮动布局就用于需要文字环绕的时候,毕竟这才是浮动真正的用武之地,水平排列的是就交给inline-block了。



node 模块简述

seo达人

Node 的os模块是操作系统的

Node 的内置模块 fs


内置模块在下载node的时候就自带的,使用 require()方法来导入

语法 :require(‘模块fs’)



在内置模块中的方法

1 fs.readFile() —》用来专门 异步 读取文件的方法 三个参数

语法 :fs.readFile(‘要读取的文件’,读取文件的格式,读取成功的回调函数)

Eg : fs.readFIle(‘a’,’utf8’,’function(err,data){ })



2 fs.readFileSync()-– 专门用来 同步 读取的方法, 两个参数

语法: fs.readFileSync(‘要读取的文件’,读取格式)



3 fs.writeFIle() —>用来写入 异步 文件的方法 三个参数

语法: fs.writeFile(‘写入到哪个文件’,写入的内容,成功的回调函数)

Eg: fs.writeFile(‘./text.tex’,”内容”, function(){ })

注意:再次写入的内容会完全覆盖 。如果文件夹没有 会自动创建一个文件夹



4 fs.writeFileSync() --> 同步写入的方法

语法: fs.writeFileSync(‘写入到文件’,“写入的内容”)



Node的http模块

这个模块专门用来创建服务的

只能支持http协议。

也是使用require()方法

Const http= require(“http”)



方法

1 http.createServer(function(req,res){ }) 两个形参

Req=request 代表每次的请求信息

Res=response 代表每次请求的响应

返回值是一个服务,当服务监听端口号的时候,就变成了服务器。

2 监听端口号

创建的服务.listen(监听的端口号,监听成功的回调函数(选填))

server.listen(8080,function(){ 端口号0-65535 建议0-1023不使用 })

此时浏览器就可以执行localhost进行访问了



自定义模块

每一个js文件都是一个独立的模块,他们都自带一个 module 是一个对象,

其中 module里面的 exports,是一个对象 这个 module.exports 就是这个文件向外导出的内容,也就是说,只有导出,才能导入



Eg: function fn1(){console.log() }

Module.exports.fn1=fn1

这样,才能是另一个js文件到入这个文件 同样也是require(‘./js’)方法


教你用面向对象编程写一个烟花爆炸的

前端达人

点击查看原图



想要学会这个漂亮的烟花吗?快来跟着学习吧~

结构

<div class="container"></div>

我们只需要一个盒子表示烟花爆炸范围就可以了

样式

fire是烟花 注意添加绝对定位

 <style>
    .container{
        margin: 0 auto;
        height: 500px;
        width: 1200px;
        background: black;
        position: relative;
        overflow: hidden;
    }
    .fire{
        width: 10px;
        background: white;
        height: 10px;
        /* border-radius: 50%; */
        position: absolute;
        bottom: 0;
    }
    </style>



行为

编写构造函数Firework

需要用到一个鼠标点击的位置,一个div选择器,一个爆炸样式

 function Firework(x,y,selector,type){
        //此处获取对象的方式为单例的思想,避免重复获取相同的元素
        if(Firework.box && selector === Firework.box.selector){
            this.box =  Firework.box.ele;
        }else{
            Firework.box = {
                ele:document.querySelector(selector),
                selector:selector
            }
            this.box = Firework.box.ele;
        }
        this.type = type;
        this.init(x,y)
    }



封装一个运动的方法
function animation(ele,attroptions,callback){
    for(var attr in attroptions){
        attroptions[attr] ={
            target:attroptions[attr],
            inow:parseInt(getComputedStyle(ele)[attr])
        } 
    }
    clearInterval(ele.timer);
    ele.timer = setInterval(function(){
        for(var attr in attroptions ){
            var item = attroptions[attr]
            var target = item.target;
            var inow = item.inow;
            var speed = (target - inow)/10;
            speed = speed>0?Math.ceil(speed):Math.floor(speed);
            if(Math.abs(target - inow) <= Math.abs(speed)){
                ele.style[attr] = target+"px";
                delete attroptions[attr];
                for(var num  in attroptions){
                    return false;
                }
                clearTimeout(ele.timer);
                if(typeof callback === "function")callback();
            }else{
                attroptions[attr].inow += speed;
                ele.style[attr]  = attroptions[attr].inow+"px";
            }
        }
    },30)
}



编写原型方法
Firework.prototype = {
        constructor:Firework,
        //初始化
        init:function(x,y){
            //创建一个烟花
            this.ele = this.createFirework();
            //xy为鼠标落点
            this.x = x ;
            this.y = y;
            //maxXy为最大运动范围
            this.maxX = this.box.offsetWidth - this.ele.offsetWidth;
            this.maxY = this.box.offsetHeight - this.ele.offsetHeight;
            //初始化结束后  烟花随机颜色
            this.randomColor(this.ele);
            //烟花升空
            this.fireworkUp(this.ele);
        },
        //创造烟花
        createFirework:function(){
            var ele = document.createElement("div");
            ele.className = "fire";
            this.box.appendChild(ele);
            return ele;
        },
        //烟花升空
        fireworkUp:function(ele){
            ele.style.left = this.x + "px";
            //此处用到刚刚封装的运动方法
            animation(ele,{top:this.y},function(){
                ele.remove();
                this.fireworkBlast()
            }.bind(this));
        },
        //烟花爆炸
        fireworkBlast:function(){
            for(var i = 0 ; i < 20; i++){
                var ele = document.createElement("div");
                ele.className = "fire";
                ele.style.left = this.x + "px";
                ele.style.top = this.y + "px";
                this.box.appendChild(ele);
                ele.style.borderRadius = "50%";
                this.randomColor(ele);
                //判定一下输入的爆炸方式是原型烟花 还是散落烟花 由此更改获取的烟花位置
                animation(ele,this.type === "circle"?this.circleBlast(i,20): this.randomPosition(),function(cale){
                    cale.remove();
                }.bind(this,ele))
            }
        },
        //圆形爆炸位置
        circleBlast:function(i,total){
            var r = 200;
            var reg = 360 / total *i;
            var deg = Math.PI / 180 *reg;
            return {
                left:r * Math.cos(deg) + this.x ,
                top:r * Math.sin(deg) + this.y 
            }
        },
        //随机颜色
        randomPosition:function(){
            return {
                left : Math.random()*this.maxX,
                top : Math.random()*this.maxY
            }
        },
        randomColor:function(ele){
            var color =  "#" + parseInt(parseInt("ffffff",16)*Math.random()).toString(16).padStart(6,0);
            return ele.style.backgroundColor = color;
        }
    }



绑定事件
document.querySelector(".container").addEventListener("click",function(evt){
    var e = evt||event;
    new Firework(e.offsetX,e.offsetY,".container","circle")
    new Firework(e.offsetX,e.offsetY,".container")
})

全部代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
    .container{
        margin: 0 auto;
        height: 500px;
        width: 1200px;
        background: black;
        position: relative;
        overflow: hidden;
    }
    .fire{
        width: 10px;
        background: white;
        height: 10px;
        /* border-radius: 50%; */
        position: absolute;
        bottom: 0;
    }
    </style>
</head>
<body>
    <div class="container"></div>
    <script src="./utils.js"></script>
    <script>

    function animation(ele,attroptions,callback){
        for(var attr in attroptions){
            attroptions[attr] ={
                target:attroptions[attr],
                inow:parseInt(getComputedStyle(ele)[attr])
            } 
        }
        clearInterval(ele.timer);
        ele.timer = setInterval(function(){
            for(var attr in attroptions ){
                var item = attroptions[attr]
                var target = item.target;
                var inow = item.inow;
                var speed = (target - inow)/10;
                speed = speed>0?Math.ceil(speed):Math.floor(speed);
                if(Math.abs(target - inow) <= Math.abs(speed)){
                    ele.style[attr] = target+"px";
                    delete attroptions[attr];
                    for(var num  in attroptions){
                        return false;
                    }
                    clearTimeout(ele.timer);
                    if(typeof callback === "function")callback();
                }else{
                    attroptions[attr].inow += speed;
                    ele.style[attr]  = attroptions[attr].inow+"px";
                }
            }
        },30)
    }  

        function Firework(x,y,selector,type){
            if(Firework.box && selector === Firework.box.selector){
                this.box =  Firework.box.ele;
            }else{
                Firework.box = {
                    ele:document.querySelector(selector),
                    selector:selector
                }
                this.box = Firework.box.ele;
            }
            this.type = type;
            this.init(x,y)
        }

        Firework.prototype = {
            constructor:Firework,
            //初始化
            init:function(x,y){
                this.ele = this.createFirework();
                this.x = x ;
                this.y = y;
                this.maxX = this.box.offsetWidth - this.ele.offsetWidth;
                this.maxY = this.box.offsetHeight - this.ele.offsetHeight;
                this.randomColor(this.ele);
                this.fireworkUp(this.ele);
            },
            //创造烟花
            createFirework:function(){
                var ele = document.createElement("div");
                ele.className = "fire";
                this.box.appendChild(ele);
                return ele;
            },
            fireworkUp:function(ele){
                ele.style.left = this.x + "px";
                animation(ele,{top:this.y},function(){
                    ele.remove();
                    this.fireworkBlast()
                }.bind(this));
            },
            //烟花爆炸
            fireworkBlast:function(){
                for(var i = 0 ; i < 20; i++){
                    var ele = document.createElement("div");
                    ele.className = "fire";
                    ele.style.left = this.x + "px";
                    ele.style.top = this.y + "px";
                    this.box.appendChild(ele);
                    ele.style.borderRadius = "50%";
                    this.randomColor(ele);
                    animation(ele,this.type === "circle"?this.circleBlast(i,20): this.randomPosition(),function(cale){
                        cale.remove();
                    }.bind(this,ele))
                }
            },
            circleBlast:function(i,total){
                var r = 200;
                var reg = 360 / total *i;
                var deg = Math.PI / 180 *reg;
                return {
                    left:r * Math.cos(deg) + this.x ,
                    top:r * Math.sin(deg) + this.y 
                }
            },
            randomPosition:function(){
                return {
                    left : Math.random()*this.maxX,
                    top : Math.random()*this.maxY
                }
            },
            randomColor:function(ele){
                var color =  "#" + parseInt(parseInt("ffffff",16)*Math.random()).toString(16).padStart(6,0);
                return ele.style.backgroundColor = color;
            }
        }

        document.querySelector(".container").addEventListener("click",function(evt){
            var e = evt||event;
            new Firework(e.offsetX,e.offsetY,".container","circle")
            new Firework(e.offsetX,e.offsetY,".container")
        })
    </script>
</body>
</html>

————————————————
版权声明:本文为CSDN博主「SpongeBooob」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41383900/article/details/105026768


NodeJS服务总是崩溃的解决办法

seo达人

许多人都有这样一种映像,NodeJS比较快; 但是因为其是单线程,所以它不稳定,有点不安全,不适合处理复杂业务; 它比较适合对并发要求比较高,而且简单的业务场景。 

在Express的作者的TJ Holowaychuk的 告别Node.js一文中列举了以下罪状: 

Farewell NodeJS (TJ Holowaychuk) 

•   you may get duplicate callbacks 
•   you may not get a callback at all (lost in limbo) 
•   you may get out-of-band errors 
•   emitters may get multiple “error” events 
•   missing “error” events sends everything to hell 
•   often unsure what requires “error” handlers 
•   “error” handlers are very verbose 
•   callbacks suck 

其实这几条主要吐嘈了两点: node.js错误处理很扯蛋,node.js的回调也很扯蛋。

 

 

事实上呢?

 


事实上NodeJS里程确实有“脆弱”的一面,单线程的某处产生了“未处理的”异常确实会导致整个Node.JS的崩溃退出,来看个例子, 这里有一个node-error.js的文件: 

 

var http = require('http');

var server = http.createServer(function (req, res) {

  //这里有个错误,params 是 undefined
  var ok = req.params.ok;

  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World
');
});

server.listen(8080, '127.0.0.1');

console.log('Server running at http://127.0.0.1:8080/');


启动服务,并在地址栏测试一下发现 http://127.0.0.1:8080/  不出所料,node崩溃了 


 

$ node node-error
Server running at http://127.0.0.1:8080/

c:githubscript
ode-error.js:5
  var ok = req.params.ok;
                     ^
TypeError: Cannot read property 'ok' of undefined
    at Server.<anonymous> (c:githubscript
ode-error.js:5:22)
    at Server.EventEmitter.emit (events.js:98:17)
    at HTTPParser.parser.onIncoming (http.js:2108:12)
    at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:121:23)
    at Socket.socket.ondata (http.js:1966:22)
    at TCP.onread (net.js:525:27)



 

怎么解决呢?


其实Node.JS发展到今天,如果连这个问题都解决不了,那估计早就没人用了。 

 

使用uncaughtException


我们可以uncaughtException来全局捕获未捕获的Error,同时你还可以将此函数的调用栈打印出来,捕获之后可以有效防止node进程退出,如: 

 

process.on('uncaughtException', function (err) {
  //打印出错误
  console.log(err);
  //打印出错误的调用栈方便调试
  console.log(err.stack);
});


这相当于在node进程内部进行守护, 但这种方法很多人都是不提倡的,说明你还不能完全掌控Node.JS的异常。 

 

使用 try/catch


我们还可以在回调前加try/catch,同样确保线程的安全。 

 

var http = require('http');

http.createServer(function(req, res) {
  try {
    handler(req, res);
  } catch(e) {
    console.log('
', e, '
', e.stack);
    try {
      res.end(e.stack);
    } catch(e) { }
  }
}).listen(8080, '127.0.0.1');

console.log('Server running at http://127.0.0.1:8080/');

var handler = function (req, res) {
  //Error Popuped
  var name = req.params.name;

  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello ' + name);
};


这种方案的好处是,可以将错误和调用栈直接输出到当前发生的网页上。 

 

集成到框架中


标准的HTTP响应处理会经历一系列的Middleware(HttpModule),最终到达Handler,如下图所示: 

\ 


这 些Middleware和Handler在NodeJS中都有一个特点,他们都是回调函数,而回调函数中是唯一会让Node在运行时崩溃的地方。根据这个 特点,我们只需要在框架中集成一处try/catch就可以相对完美地解决异常问题,而且不会影响其它用户的请求request。 

事实上现在的NodeJS WEB框架几乎都是这么做的,如 OurJS开源博客所基于的 WebSvr 

就有这么一处异常处理代码: 

 

Line: 207

  try {
    handler(req, res);
  } catch(err) {
    var errorMsg
      = '
'
      + 'Error ' + new Date().toISOString() + ' ' + req.url
      + '
'
      + err.stack || err.message || 'unknow error'
      + '
'
      ;

    console.error(errorMsg);
    Settings.showError
      ? res.end('<pre>' + errorMsg + '</pre>')
      : res.end();
  }


那么不在回调中产生的错误怎么办?不必担心,其实这样的node程序根本就起不起来。 

此外node自带的 cluster 也有一定的容错能力,它跟nginx的worker很类似,但消耗资源(内存)略大,编程也不是很方便,OurJS并没有采用此种设计。 

 

守护NodeJS进程和记录错误日志


现 在已经基本上解决了Node.JS因异常而崩溃的问题,不过任何平台都不是100%可靠的,还有一些错误是从Node底层抛出的,有些异常 try/catch和uncaughtException都无法捕获。之前在运行ourjs的时侯,会偶尔碰到底层抛出的文件流读取异常,这就是一个底层 libuv的BUG,node.js在0.10.21中进行了修复。 

面对这种情况,我们就应该为nodejs应用添加守护进程,让NodeJS遭遇异常崩溃以后能马上复活。 

另外,还应该把这些产生的异常记录到日志中,并让异常永远不再发生。 

 

使用node来守护node


node-forever 提供了守护的功能和LOG日志记录功能。 

安装非常容易 

 

[sudo] npm install forever


使用也很简单 

 

$ forever start simple-server.js
$ forever list
  [0] simple-server.js [ 24597, 24596 ]


还可以看日志 

 

forever -o out.log -e err.log my-script.js


 

使用shell启动脚本守护node


使用node来守护的话资源开销可能会有点大,而且也会略显复杂,OurJS直接在开机启动脚本来进程线程守护。 

如在debian中放置的 ourjs 开机启动文件: /etc/init.d/ourjs 

这个文件非常简单,只有启动的选项,守护的核心功能是由一个无限循环 while true; 来实现的,为了防止过于密集的错误阻塞进程,每次错误后间隔1秒重启服务 

 

WEB_DIR='/var/www/ourjs'
WEB_APP='svr/ourjs.js'

#location of node you want to use
NODE_EXE=/root/local/bin/node

while true; do
    {
        $NODE_EXE $WEB_DIR/$WEB_APP config.magazine.js
        echo "Stopped unexpected, restarting 

"
    } 2>> $WEB_DIR/error.log
    sleep 1
done


 

错误日志记录也非常简单,直接将此进程控制台当中的错误输出到error.log文件即可: 2>> $WEB_DIR/error.log  这一行, 2 代表 Error。

产品分析方法之:情绪版在设计中的运用

鹤鹤

视觉设计师可能会花很长时间产出了精致的,高品质的设计,得到的却是用户或客户的一句话:“这不是我想要的!”

视觉设计师可能会花很长时间产出了精致的,高品质的设计,得到的却是用户或客户的一句话:“这不是我想要的!”一般来说,在没有实物前,人们并不清楚自己要的是什么。但是在看到成品后,他们可以轻易地判断是否符合自己的喜好或期望。因此,在为错误的设计方向投入过多前,了解用户对风格的期望和需求,从而确定整个网站或产品的视觉风格是有必要的。  而情绪版可以很好的解决以上问题。




什么是情绪版?

情绪版是一种启发式和探索性的方法,可以对如下问题进行研究:图像风格(photography style)、色彩(color palettes)、文字排版(typography)、图案(pattern)以及整体外观以及感觉。视觉设计和人的情绪紧密相连,不同的设计总是会引发不同的情感。




情绪版分为:拼贴式情绪版、参考式情绪版和模版式情绪版



拼贴式情绪版

直接将可以运用到项目中的图片素材拼合在一起。下图是NIKE MECURIAL系列的一个拼贴式情绪版,里面的图片都是来自官方的海报、图片和影像,这些素材都能够直接运用到我们的设计中。


参考式情绪版

将与设计主题风格相关或功能类似的真实项目拼接在一起。下图是一个以多彩和现代视觉风格的情绪版,里面选取的素材都是真实项目的界面。


模版式情绪版

灵感图片素材和概念控件的结合。概念控件是指概念设计中的一些核心组件,它可以是色彩搭配、按钮、卡片、图形或者是信息排版等,它们是概念设计的雏形。


情绪版作为可视化的沟通工具,可以快速地向他人传达设计师想要表达的整体感觉。


设计师要帮助用户发掘其真正需求,情绪版作为一个工具可以很好的帮助了解用户所希望展现的调性,从而提高生产效率和满意度。


对于设计师:是定义视觉风格和指导设计方向的依据;对团队:在团队之间传递设计灵感与设计思路,从而使想法充分融合,深化设计。


1.情绪版可以让客户参与我们的设计流程中,提高我们的工作效率。客户的加入,能够让我们更好的了解业务本身以及客户对项目的期待。尽早的让客户参与整个设计流程,还能够避免在错误的设计方向上投入过多。

2.情绪版是设计与客户沟通的可视化沟通工具,可以减少设计师和客户之间由于认知不同导致的沟通障碍。



情绪版的推导过程







在制作情绪版过程中,原生关键词的作用相当重要:

获得原生关键词是情绪版的第一项工作,一般从内部涉众(相关的产品和设计人员)及外部用户两种渠道获得。

自涉众访谈和用户研究中,可以收集大量的体验词样本。在获得这些样本后,可以内部进行讨论,通过归纳整理精简为几个关键词。

原生关键词提取好之后,可以在内部使用情绪版,也可以招募用户来完成。





01.明确原生关键词


访谈及用研结果导入产生原生体验关键词

原生关键词(Primary keywords)的产生是一个糅合的过程,它需要综合企业文化、用户研究成果、品牌/营销策略,行业特征、目标用户群、产品的价值定位等因素来界定,通常,这也会是一个商业决定。

涉及的访谈受众可以是产品、运营、交互、视觉、用户等,根据产品是0—1还是改版现有版本设置不同问题,以改版为案例,产品使用的感受、期望产品更新的样子等。以此得出的关键词,一般是很抽象的词汇。例如:亲切、熟悉、温度,一般一个产品的关键词不易过多3~5个为佳。




项目案例(示例)

某手机银行是以服务客户日常金融业务办理及投资理财需求为主的综合金融服务APP。面向30-40岁为主要年龄段的中青年用户群体。目前产品处于成长期向成熟期过渡。








02.挖掘衍生关键词


如果仅通过单纯对原生关键词的搜索,很容易导致不同参与者提供图片素材出现同质化的问题。所以,首先头脑风暴画出关键词的思维导图。一方面,合理地引导调研对象发散思路;另一方面,也在过程中深挖原生关键词在他们心中的定义。


衍生关键词(Derived keywords)是原生关键词的发散和提炼,主要通过部门内部头脑风暴或用户访谈得出。


将所有“衍生关键词”按照三个维度去分类整理。这个过程的目的是帮助项目组成员从用户的角度去理解“抽象关键词”的“具象诠释”。所有的关键词可按照以下三个维度分类:


访谈对象会根据主观印象以及过往亲身经历给出一些看法或答案,而很多时候并不可以把访谈对象的答案直接作为关键词,我们需要了解为什么,直到觉得用户的答疑非常清晰具体,然后提炼关键词。


例如衍生关键词访谈:

自由发散问题—看到“品质”你想到了什么?

引导发散问题—如果“品质”是一种颜色,你觉得是什么?为什么?

如果“品质”是一种食物,你觉得是什么?为什么?

如果“品质”是……



衍生关键词的分析—分维诠释

根据原生关键词的的定义,从视觉映射、心境映射、物化映射三个维度去理解“抽象关键词”的“具象诠释”。





03.搜集图片素材


根据“原生关键词”及发散的“衍生关键词”搜集素材,对应视觉映射、心理映射、物化映射三大维度。在素材搜集时具体以「具象」和「抽象」两个方向搜集。

1.搜集图片

根据已有关键词,搜集具象图片(具体的实物场景)—风格感受;根据已有的关键词,搜集抽象图片(包含色彩、质感、图形等元素)—设计元素。


2.素材整理

将收集到的图片素材,按照衍生关键词进行分类并提取生成情绪版。


3.邀请用户参与情绪版创建



1.主持人需要不断询问被访者,去探究选择图片背后的原因:“为什么你会选择这张图片?能否和大家分享一下你的想法?”

2.注意差异的挖掘。注意挖掘被访者之间的观点差异,一百个人心中有一百个哈雷姆特,同一张图片对于不同被访者可能会有不同的解释,如果好几位被访者同时选择一张图片代表他们各自对某个品牌的感觉,注意询问他们选择这张图片的原因是否一样。

3.可以呈现给用户的图片有限的,因此,在挑选图片时,需要内部研究和设计人员协同,根据视觉设计所需要考虑的几个维度结合已有的关键词进行图片的筛选。一般来说,在将图片呈现给用户之前,内部人员已经明确了每一张图片所代表的意义,在用户选择和访谈结束后,两方面的数据综合分析才能获得最终的结果。





04.创建生成情绪版


归纳和整理图片,进行排版组成情绪版,得到设计主题相关的内容。建立几个统一风格的情绪版,以便更好的捕捉产品相关的感觉,为探索设计方向提供灵感。 




05.确定视觉设计策略


综合情绪版制定风格

提取图片主要颜色,明确主色。结合衍生关键词分析结果,将情绪版中高频物化纹理和材质提取出来。



1.色彩提取

通过对色彩的分析发现,高明度低饱和度的色彩搭配,能让画面保持丰富的同时显得干净和协调,可以达到「」「简洁」的效果,例如:邻近色、类似色、低饱和度对比色。


电子化情绪版对“色彩分析”是比较方便的

1.图片在PS中进行高斯模糊或马赛克处理,使用颜色滴管提取大色块;2.图片导入PS中,选择存储为Web所用格式-选择Gif仿色。当然,现在已经有很多用户配色方案提取的网站和软件,这样更事半功倍。


通过对情绪版中颜色提取并结合品牌色及对当下流行趋势的把握,确定如下颜色运用:




2.图形提取

通过对图形分析发现,基本几何形具有肯定、纯粹的特点,可以很好的体现「品质」与「精简」的特征。例如:方形、圆形等。


融入图形符号 强化视觉语言

图标使用深色+渐变色的展现,对比突出、品牌属性统一、信息层级分明。简约笔挺的线性图标更符合平台信息架构清晰,内容易懂的特征。



图标设计




3.字体提取

通过对字体的研究发现,中文字体端庄匀称、字体方正。例如:思源宋体、方正宋体等,英文字体线条简洁、字形严谨。例如:Helvetica、Avenir、DIN等,都比较符合「品质」和「简洁」的特征。

笔画有粗细变化,而且一般是横细竖粗,末端有装饰部分,给人正式、正规感觉,既可以区分标题与模块内容的差别,也可以增加产品的差异化。





4.构成提取

通过对构成的研究发现,并置型和九宫格型构成比较严谨和秩序,满版型和通栏型构成,视觉传达直观而强烈,给人大方、舒展的感受;这与「」的特征是匹配的。






5.质感提取

在质感的选择方面,大多与当下流行风格趋势相贴近,例如:圆角卡片、弥散投影、渐变、轻拟物、毛玻璃等,可以有效地表达出「精简」「品质」的情绪感受。





总结

情绪版是一种设计方法论,可以指导设计方向,传递设计灵感与思路。

制作情绪版时,首先要明确原生关键词、然后头脑风暴挖掘衍生关键词,接着搜集相关图片并提取生成情绪版,另外访谈用户收集衍生关键词映射,最后通过情绪版和关键词映射来提取视觉风格。

在项目前期,我们可以通过情绪板来定义产品整体的设计风格和产品主色调,设计过程中,界面排版、图标的颜色、形状和设计细节都可以使用情绪板来定义。

无论我们做什么样的设计,情绪板都无疑是一个很好的工具,它不仅可以帮助我们明确设计需求,做出更容易被大众所接受的设计,也可以帮助我们更好地去与领导和其他需求方沟通,向他们传达设计的价值主张,最终达成共识,提高设计效率。


再总结一下情绪板的作用:

首先:它是整个设计项目的宝贵资源;

其次:它是低成本的设计工具;

再次:它是有效沟通的保障;

最后:它是团队协作的方法。


转自:站酷-体验为王UX

JSP的刨根问底

前端达人

JSP的刨根问底

  • 一.概念
  • 二.原理
  • 三.脚本
  • 四.内置对象
  • 五.注释
  • 六.编译指令
  • 七.动作指令


  • 点击查看原图


    这篇文章从一个初学者的角度,复习一波JSP。






    一.概念
    Java Server Pages: java服务器端页面

    可以理解为:一个特殊的页面,其中既可以指定定义html标签,又可以定义java代码
    用于简化书写!!!
    二.原理
    本质是一个Servlet

    三.脚本
    定义:Jsp定义java代码的方式

    <% 代码 %>:定义的java代码,在service方法中。service方法中可以定义什么,该脚本中就可以定义什么。
    <%! 代码 %>:定义的java代码,在jsp转换后的java类的成员位置。
    <%= 代码 %>:定义的java代码,会输出到页面上。输出语句中可以定义什么,该脚本中就可以定义什么。
    四.内置对象
    对象名 实现接口或继承类 作用
    PageContext 当前页面共享数据,还可以获取其他八个内置对象
    request HttpServletRequest 一次请求访问的多个资源(转发)
    session HttpSession 一次会话的多个请求间
    application ServletContext 所有用户间共享数据
    response HttpServletResponse 响应对象
    page Object 当前页面(Servlet)的对象 this
    out JspWriter 输出对象,数据输出到页面上
    config ServletConfig Servlet的配置对象
    expection Throwable 异常对象
    五.注释
    1.html注释
    <!-- -->:只能注释html代码片段
    2.jsp注释
    <%-- --%>:可以注释所有

    六.编译指令
    作用
    用于配置JSP页面,导入资源文件

    格式
    <%@ 指令名称 属性名1=属性值1 属性名2=属性值2 ... %>
    分类
    1.page
    配置JSP页面的
    1. contentType:等同于response.setContentType()
    *设置响应体的mime类型以及字符集
    * 设置当前jsp页面的编码(只能是高级的IDE才能生效,如果使用低级工具,则需要设置pageEncoding属性设置当前页面的字符集)
    2.import:导包
    3. errorPage:当前页面发生异常后,会自动跳转到指定的错误页面
    4.isErrorPage:标识当前也是是否是错误页面。
    * true:是,可以使用内置对象exception
    * false:否。默认值。不可以使用内置对象exception
    2.include
    页面包含的。导入页面的资源文件

     <%@include file="top.jsp"%>
    3.导入资源
    导入资源

     <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
      prefix:前缀,自定义的
    1
    七.动作指令
    作用
    动作指令与编译指令不间,编译指令是通知 Servlet 引擎的处理消息,而动作指令只是运行时的脚本动作。编译指令在将JSP 编译成 Servlet 时起作用:处理指令通常可替换成 Java脚本,是 JSP脚本的标准化写法。

    分类
    指令 作用
    jsp:forward 执行页面转向,将请求的处理转发到下一个页面。
    jsp:param 用于传递参数,必须与其他支持参数曲标签一起使用。
    jsp:include 用于动态引入一个 JSP 页面。
    jsp:plugin 用于下载 JavaBean 或 Applet 到客户端执行。
    jsp:useBean 使用 JavaBean。
    jsp:setProperty 修改 JavaBean 实例的属性值。
    jsp:getProperty 获取 JavaBean 实例的属性值。
    具体细节:
    jsp:forward:
    动作把请求转到另外的页面。可以转发静态的HTML页面,也可以转发动态的JSP页面,或者转发到容器中的servlet jsp:forward标记只有一个属性page。 page属性包含的是一个相对URL。 page的值既可以直接给出,也可以在请求的时候动态计算。

    jsp:param
    用于设定参数值,这个指令不能单独使用 可以与以下三个指令结合使用:
    jsp:include :用于将参数值出入被导入页面
    jsp:forword : 用于将参数值传入被转向页面
    jsp:plugin : 用于将参数值传入页面中javaBean的实例

    jsp:include
    (拿目标页面插入原有页面)该动作是一个动态的include指令,也用于带入某个页面,他不会导入被include页面的编译指令,仅仅导入页面的body内容插入到本页面
    该动作把指定文件插入正在生成的页面。其语法如下:   flush:用于指定输出缓存是否转移到被导入的文件中, true:包含在被导入的文件中 false:包含在源文件中
    前面已经介绍过include指令,它是在JSP文件被转换成Servlet的时候引入文件,而这里的jsp:include动作不同,插入文件的时间是在页面被请求的时候。j
    sp:include动作的文件引入时间决定了它的效率要稍微差一点,而且被引用文件不能包含某些JSP代码(例如不能设置HTTP头),但它的灵活性却要好得多。
    jsp:plugin

    用于下载服务器端的javaBean或applet到客户端)
    jsp:plugin动作用来根据浏览器的类型,插入通过Java插件运行Java Applet所必需的OBJECT或EMBED元素。

    <jsp:plugin  
      type="bean | applet"   : 被执行的java程序的类型
      code="classFileName" :被执行的文件名,必须以  .class 结尾
      codebase="classFileDirectoryName"   :被执行文件的所在目录
      [ name="instanceName" ] :给程序起一个名字用来标识该程序
      [ archive="URIToArchive, ..." ] :指向一些要预先载入的将要使用到的类
      [ align="bottom | top | middle | left | right" ] :
      [ height="displayPixels" ]
      [ width="displayPixels" ]
      [ hspace="leftRightPixels" ] 
      [ vspace="topBottomPixels" ]
      [ jreversion="JREVersionNumber | 1.1" ]   :能正确运行改程序必须的JRE的版本
      [ nsplug inurl="URLToPlugin" ]  
      [ iepluginurl="URLToPlugin" ] >
      [ <jsp:params>
      [ <jsp:param name="parameterName" value="{parameterValue | <%= expression %>}" /> ]+
      </jsp:params> ]
      [ <jsp:fallback> text message for user </jsp:fallback> ]  :当不能正确显示该Applet时,显示该指令中的文本提示
     <jsp:plugin>
    



    jsp:useBean
    useBean:用于在jsp页面中初始化一个java实例(如果多个jsp中需要重复使用某段代码,可以把这段代码定义成java类在页面中引用)
    jsp:useBean动作用来装载一个将在JSP页面中使用的JavaBean。这个功能非常有用,因为它使得我们既可以发挥Java组件重用的优势,同时也避免了损失JSP区别于Servlet的方便性。
    jsp:useBean动作最简单的语法为:<jsp:useBean id=“JavaBean的名称” class=“package.class"包名.类名” scope=“有效范围”/>

    这行代码的含义是:“创建一个由class属性指定的类的实例,然后把它绑定到其名字由id属性给出的变量上”。不过,就象我们接下来会看到的,定义一个scope属性可以让Bean关联到更多的页面,它可接受四个值:request、session、page、application。此时,jsp:useBean动作只有在不存在同样id和scope的Bean时才创建新的对象实例,同时,获得现有Bean的引用就变得很有必要。
    获得Bean实例之后,要修改Bean的属性既可以通过jsp:setProperty动作进行,也可以在Scriptlet中利用id属性所命名的对象变量,通过调用该对象的方法显式地修改其属性。这使我们想起,当我们说“某个Bean有一个类型为X的属性foo”时,就意味着“这个类有一个返回值类型为X的getFoo方法,还有一个setFoo方法以X类型的值为参数”。  有关jsp:setProperty动作的详细情况在后面讨论。但现在必须了解的是,我们既可以通过jsp:setProperty动作的value属性直接提供一个值,也可以通过param属性声明Bean的属性值来自指定的请求参数,还可以列出Bean属性表明它的值应该来自请求参数中的同名变量。
    在JSP表达式或Scriptlet中读取Bean属性通过调用相应的getXXX方法实现,或者更一般地,使用jsp:getProperty动作。
    注意,包含Bean的类文件应该放到服务器正式存放Java类的目录下,而不是保留给修改后能够自动装载的类的目录。例如,对于Java Web
    Server来说,Bean和所有Bean用到的类都应该放入classes目录,或者封装进jar文件后放入lib目录,但不应该放到servlets下。
    id:javaBean的实例名 class: javaBean的实现类 scope:指定javaBean实例的生存范围
    page:javaBean仅在该页面有效 request:javaBean在本次请求中有效 session:
    javaBean在本次session内有效 application:
    javaBean在本应用内一直有效  下面是一个很简单的例子,它的功能是装载一个Bean,然后设置/读取它的message属性。
    关于jsp:useBean的进一步说明   使用Bean最简单的方法是先用下面的代码装载Bean:   <jsp:useBean id=“name” class=“package.class” />
    然后通过jsp:setProperty和jsp:getProperty修改和提取Bean的属性。 不过有两点必须注意。   第一,我们还可以用下面这种格式实例化Bean: <jsp:useBean …>   Body   </jsp:useBean>
    它的意思是,只有当第一次实例化Bean时才执行Body部分,如果是利用现有的Bean实例则不执行Body部分。正如下面将要介绍的,jsp:useBean并非总是意味着创建一个新的Bean实例。
    第二,除了id和class外,jsp:useBean还有其他三个属性,即:scope、type、beanName。
    id:命名引用该Bean的变量。如果能够找到id和scope相同的Bean实例,jsp:useBean动作将使用已有的Bean实例而不是创建新的实例。
    class:指定Bean的完整包名。
    scope:指定Bean在哪种上下文内可用,可以取下面的四个值之一:page、request、session和application。  默认值是page,表示该Bean只在当前页面内可用(保存在当前页面的PageContext内)。
    request表示该Bean在当前的客户请求内有效(保存在ServletRequest对象内)。
    session表示该Bean对当前HttpSession内的所有页面都有效。
    最后,如果取值application,则表示该Bean对所有具有相同ServletContext的页面都有效。
    scope之所以很重要,是因为jsp:useBean只有在不存在具有相同id和scope的对象时才会实例化新的对象;
    如果已有id和scope都相同的对象则直接使用已有的对象,此时jsp:useBean开始标记和结束标记之间的任何内容都将被忽略。
    type:指定引用该对象的变量的类型,它必须是Bean类的名字、超类名字、该类所实现的接口名字之一。请记住变量的名字是由id属性指定的。
    beanName:指定Bean的名字。如果提供了type属性和beanName属性,允许省略class属性。

    jsp:setProperty
    jsp:setProperty用来设置已经实例化的Bean对象的属性,有两种用法。
    首先,你可以在jsp:useBean元素的外面(后面)使用jsp:setProperty,    …

    第二种用法是把jsp:setProperty放入jsp:useBean元素的内部, …

    jsp:setProperty动作有下面四个属性:name:表示要设置属性的是哪个Bean。  property:表示要设置哪个属性。有一个特殊用法:如果property的值是"",表示所有名字和Bean属性名字匹配的请求参数都将被传递给相应的属性set方法。  value:value属性是可选的。该属性用来指定Bean属性的值。字符串数据会在目标类中通过标准的valueOf方法自动转换成数字、boolean、Boolean、byte、Byte、char、Character。例如,boolean和Boolean类型的属性值(比如“true”)通过Boolean.valueOf转换,int和Integer类型的属性值(比如"42")通过Integer.valueOf转换。  value和param不能同时使用,但可以使用其中任意一个。  Param:param是可选的。它指定用哪个请求参数作为Bean属性的值。如果当前请求没有参数,则什么事情也不做,系统不会把null传递给Bean属性的set方法。因此,你可以让Bean自己提供默认属性值,只有当请求参数明确指定了新值时才修改默认属性值。  例如,下面的代码片断表示:如果存在numItems请求参数的话,把numberOfItems属性的值设置为请求参数numItems的值;否则什么也不做。    如果同时省略value和param,其效果相当于提供一个param且其值等于property的值。进一步利用这种借助请求参数和属性名字相同进行自动赋值的思想,你还可以在property(Bean属性的名字)中指定“”,然后省略value和param。此时,服务器会查看所有的Bean属性和请求参数,如果两者名字相同则自动赋值。  下面是一个利用JavaBean计算素数的例子。如果请求中有一个numDigits参数,则该值被传递给Bean的numDigits属性;numPrimes也类似。  JspPrimes.jsp

    20200321215032100.jpg
    jsp:getProperty
    jsp:getProperty动作提取指定Bean属性的值,转换成字符串,然后输出。
    jsp:getProperty有两个必需的属性,即:name,表示Bean的名字;property,表示要提取哪个属性的值。

    END!!!有什么意见可以提出来!
    长路漫漫,JAVA为伴!!!
    ————————————————
    版权声明:本文为CSDN博主「福尔摩东」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_43688587/article/details/105017469

label和span标签设置宽度width无效的解决办法

前端达人

如何用CSS使label标签等宽?

<div><label>密码</label><input type="password" /></div>
<div><label>确认密码</label><input type="password" /></div>
如何对齐呢?加空格的方法不好哦。(因为label中的字数不一样,所以页面显示效果不对齐)
问题原因及解决办法:

label默认为内联元素,元素前后没有换行符,并且不可以设置宽度。

如果要为label设置宽度,则需要改变label的display属性,使其变为一个块级元素。

方法如下:

1.增加inline-block属性值,将label标签变为行内块元素(css2.1新增)

     {display:inline-block;}

2.增加width属性,如将宽度设置为100px
    label{width:100px;display:inline-block;}

3.添加好后页面上所有的label标签会变为100pxv



JavaScript中的混淆器

随着 AJAX 和富界面技术的发展,Javascript 在 Web 应用上的重要性越来越高,Javascript 代码的复杂性、功能和技术含量也越来越高,对Javascript 代码保护的需要也越来越迫切。



压缩 compress: 去掉空格,换行,注释等,格式紧凑,节约存储空间。



混淆 obfuscate/garble:替换变量名或方法名,让js不容易看懂。也做到了压缩的效果。



加密 encrypt:一般用eval方法加密,效果与混淆相似。也做到了压缩的效果。



我们可以通过工具进行混淆,通过工具也是目前最好的方式。推荐一个很好的混淆工具: JSObfuscator By Unest


20200321102359879.png


————————————————

版权声明:本文为CSDN博主「刘亦枫」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/liuyifeng0000/article/details/105004732




日历

链接

个人资料

蓝蓝设计的小编 http://www.lanlanwork.com

存档