首页

超全面!「无障碍设计」指南

分享达人

你知道吗?视力,听力和行动能力完全健康的人,可以轻松地读写,可以有效执行多任务,并且始终可以正常工作的人约占总人口的50%?其余的人都是戴着眼镜或有色盲,手腕或耳朵受伤,生活在嘈杂的环境中或网络信号质量差,忙碌或忙碌中,阅读障碍或有注意力障碍等。


这意味着大约一半的用户可能很难使用我们的产品或浏览我们的网站。因此,我们可能错过了提高用户满意度并扩大受众范围的机会。

不过在设计阶段实施一些简单的原则就可以改善交互和整体用户体验,极限设计可以为所有人带来价值,我们称之为“包容性设计”。


什么是包容性设计?包容性设计考虑了尽可能多的人的需求和能力,而不仅仅是针对残疾人。它认识到我们的需求会随着时间和环境的变化而变化,因此它会预测错误,挣扎和不同的交互方式。它的目的是在问题发生之前解决问题,提高标准并改变良好产品设计的标准。


包容的用户界面是善解人意,有意识且可访问的。年龄,性别,教育程度,财富和能力等不同特征,在不同环境中生活或工作,获得技术水平不同的不同人群可以舒适地使用它。


我们将使用POUR作为在用户与界面之间创建简单,轻松,快速交互的参考。


POUR代表

  • 可以理解:数字内容可以轻松地以不同方式进行解释或处理吗?

  • 可操作:数字产品能否轻松自如地进行功能和控制?

  • 可以理解:用户可以理解界面的功能和内部信息吗?

  • 健壮性:数字产品是否与不同的辅助技术和设备兼容?


设计师如何提供帮助

作为设计师,我们当然不能控制以上所有要求都能做到。但是我们应该承认,人们遇到的许多可访问性问题是由设计阶段未做过的决定引起的。因此,设计师有很多机会可以有所作为。仅通过做出更明智的设计决策,我们就可以影响(改进或协助)四种经验。


  • 视觉体验:这包括形状,颜色,对比,文本样式-产品界面的所有图形元素。

  • 听觉体验:这是指与产品互动时产生的声音,音量和清晰度。

  • 认知经验:这描述了用户花费在解释界面上的时间,以及使用界面需要多少注意力和精力。

  • 运动体验:这包括执行任务或与产品交互所需的所有动作和动作。


通常,可访问性被认为是对创造力的挑战;但是,如果我们认为这是一个创造性的挑战,那么我们会开辟全新的可能性领域。真正好的可访问性的诀窍不是在功能或功能上进行折衷,也不是在美学上取舍,而是使功能和创意体验也可以访问。


改善视觉体验


1.颜色

对比度对比度是亮度或颜色的差异,使物体从周围环境中脱颖而出,并可能对清晰度产生显着影响。高对比度使视觉元素从背景中脱颖而出,更加引人注目。

专家提示:纯粹的#000000黑白色会给眼睛带来强烈的对比度,甚至会影响阅读障碍者。这就是为什么我们倾向于避免使用它,而是选择深灰色的原因。


亮度

亮度描述从光源发出的照明水平或从表面反射的光量。明亮的颜色反射更多的光线,并会干扰我们阅读和处理信息的能力。


避免在背景或较大表面上使用鲜艳的颜色。请勿在文本上或文本附近使用鲜艳的颜色,以免干扰文本。如果品牌要求特定的高亮度颜色,请尝试建议使用饱和或较深的颜色。如果你绝对必须使用明亮的颜色,则应将其用于突出显示动作的方法最小化,并将其与较深的色相搭配以达到平衡和高对比度。


专家提示:任何含有超过50%黄色的颜色都会自然反射更多的光。这意味着黄色,橙色,绿色和蓝绿色是高风险颜色,应谨慎使用。


色盲

色盲是无法区分特定颜色(通常是红色和绿色,偶尔是蓝色)的一种,它比你想象的要常见。

专家提示:不要仅仅依靠颜色;颜色不应该是传达重要信息的唯一方法。您可以执行以下操作:

  • 使用下划线表示链接和斜体,或使用粗体突出显示文本

  • 将图标与文本一起使用可传达成功或失败的信息

  • 使用纹理或图案作为图表

  • 为按钮或明显的通知使用清晰的视觉样式,针对焦点或活动状态使用不同的字体样式


2.版式

字体选择

通信是每个数字产品的首要目标,可以借助印刷术及其正确应用来实现。内容应清晰易读,这意味着易于识别和解释,轻松阅读和处理。

简洁明了对于快速阅读和解释至关重要,请避免使用复杂的字体,因为它们只会增加视觉干扰。选择正确的字体家族,针对那些具有清晰定义和独特形状的字符,因为视力障碍或阅读障碍的人可能会因某些字符或其组合而感到困惑。


字体样式

字体样式还会影响弱视或阅读障碍者的阅读性能。我们应该注意并谨慎使用字体样式(如斜体,下划线和大写)的频率和位置。

根据“英国阅读障碍协会”的规定,应避免使用斜体,特别是对于较大的副本块或较小的字体。这是因为它们使字母倾斜,显得更加尖锐,因此更难以阅读。


正文也应避免使用带下划线的字体样式。给长的段落加下划线会增加视觉噪音,从而降低阅读性能,而给短的句子或单词加下划线会与活动链接相关联,并可能引起混乱。粗体是添加对比度和强调的更好选择。


尽管没有确凿的研究,但有一些证据支持也应避免主要针对正文使用大写字母。似乎所有大写字母的统一外观会降低单词形状的对比度,从而使扫描变得不那么容易。此外,大写看起来有点紧张,可能感觉好像有人在向您大喊大叫。

专家提示:平衡是关键。谨慎使用每个样式并赋予其含义甚至可以提高可读性。


字体大小

您知道绝大多数人戴眼镜或隐形眼镜吗?实际上,十分之六以上!此外,约有62%的人通过手机访问互联网,这还不包括应用程序的使用情况。当视力不佳的人在旅途中在小屏幕上使用技术时,可能会出什么问题?

使用较大的字体。通常,16px被认为是最具有包容性的,但是请注意,字体可以以不同的比例站立,并且字体的大小可以相差很大。切勿低于14px,事实上,大多数现代网站的正文都使用18px字体,而标签,标题或工具提示仅使用14px或16px。

专家提示:此外,避免使用薄而轻的字体,因为对于较小的字体或在明亮的光线下可能难以阅读。


段落格式

帮助人们轻松浏览内容应该是我们的首要目标,因为只有20%的人可以阅读内容,其中55%的人可以快速浏览内容。我们的工作是通过使用舒适的段落格式来尽可能地支持人们。


研究表明,用于支持可读性的平均在线行长(包括空格)约为70个字符。标题,字幕和项目符号点将有助于扫描,而左段对齐将使文本更易于阅读。


较长的文字墙使人们参与的机会大大减少。成功的段落长度不超过5到6个句子。

空格将帮助患有认知和注意力障碍的人,保持阅读重点。对于其余的内容,它只会使阅读更加愉快和流畅。根据WCAG,最佳做法是将行高(行之间的间距)设置为相对于该类型大小的1.5相对值。段落之间的间距也至少应比行间距大1.5倍,因此必须明确定义。


提示:行距不应超过2.0,因为它可能产生相反的效果并分散读者注意力。


复制版面

作为设计师,我们经常陷入过度设计布局的陷阱,以使它们看起来引人注目或独特,从而将可用性放在一边。这就是为什么我们看到诸如文本的一部分之类的趋势在彩色或带纹理的背景上重叠图像或文本的趋势。只要我们知道如何以及何时使用它们,我们仍然可以享受其中的一些趋势。

当在彩色或带纹理的背景上使用文本时,我们需要确保它们之间的色彩对比度足够高,同时在整个重叠区域都保持一致-意味着在副本下没有较浅和较暗的区域,也没有过多的细节干扰。较大的字体大小和较重的字体粗细也会提高对比度。


专家提示:一如既往地“了解您的用户”。时髦的布局并不适合所有人。


改善听觉体验


您可能在想,视觉设计如何影响听觉体验?因此,想象一下您正在与一个俱乐部的朋友交谈。我敢打赌,您只能听见她说的话的一半,但是您可以通过看着她的嘴唇移动,肢体语言和面部表情来保持对话的进行。由于视觉效果的支持增强了模棱两可的声音,因此您最终可以理解它们。


在用户界面中,声音对于不同的人可能意味着各种各样的事情。它们也很容易在嘈杂的背景中丢失,因此最好以视觉提示来支持它们。

我们的目标应该是提供听觉和视觉提示的反馈,支持错误,通知以及与相关和邻近图形元素的重大交互。我们还必须确保视觉线索保持足够长的活动时间,以使人们能够看到和阅读,同时又不隐藏任何重要的内容。


一个好的做法-不限于支持声音辅助技术,是在UI元素中添加描述性标签,并在图像中添加标题,以便于在屏幕阅读器中轻松导航。为视频使用字幕是改善听力体验的另一种方法,对非母语人士也有帮助。


最后,我们不应该忽略声音是问题的情况,这就是为什么我们需要视觉替代的原因。有些人可能对特定的声音敏感,或者处于声音可能引起干扰的情况下。然后,这是一个好习惯,让人们可以选择关闭声音而不必调低扬声器音量,从而使此功能清晰可见。


专家提示:避免使用不必要的自动播放声音和音乐,因为它们会打扰甚至惊吓别人。


改善认知体验


1.知觉

视觉清晰度

清晰度是用户界面中的第一个也是最重要的属性。成功的用户界面使用户能够识别和解释他们所看到的内容,了解产品的价值和所要采取的行动,预测使用产品时会发生什么以及与产品成功交互。

  • 形式跟随功能是一项原则,指出对象应反映其预期的功能或目的。为了在用户界面中实现此目的,我们使用了附加功能,附加到UI的视觉提示/属性,以显示用户与其交互的可能方式。

    支付能力取决于用户的身体能力,目标,过去的经验,当然还取决于他们认为可能的情况。按钮应该看起来像按钮,就像链接,菜单标签,表单等一样。使用清晰的符号/功能可以帮助用户识别或解释界面,并轻松进行交互。


  • 在用户界面中使用熟悉的和已建立的设计解决方案将帮助用户预测结果并自信地采取行动。因此,使用设计模式来解决常见问题是一个好习惯,该设计模式是经过测试,优化和可重用的解决方案。

    设计模式建立在过去的经验和可能性的基础上,并附加到特定的目标上。为避免眼前的问题,选择正确的设计模式应该是我们避免混淆或压力大的交互的第一要务。


  • 建立一致的视觉语言是获得更全面界面的关键。具有相同功能和/或重要性的重复交互式UI组件应始终以相同的方式外观和操作。因此,导航,按钮,链接,标签,错误等元素应在整个产品中具有一致的样式,颜色和动画。
    值得注意的是,一致的视觉语言不仅可以通过附加含义和减少视觉噪音来帮助互动,而且还可以增强产品的个性,提升品牌知名度,建立情感联系和信任。


层次结构

视觉层次结构是指图形元素的视觉重量及其排列方式,使用户可以轻松地探索和发现内容。通过为页面元素分配不同的视觉权重,我们可以对内容进行分组并影响人们感知信息和浏览产品的顺序。

  • 颜色是第一大关注焦点。彩色元素将脱颖而出,因此在层次结构中位于较高位置。明亮的颜色会更加突出,因此,考虑到这一点,我们应该仔细安排和分配颜色,以将眼睛引导至正确的位置。

  • 视觉元素的大小(例如印刷,按钮,图标和图像)在确定重要性方面几乎与颜色一样强大。较大的图形吸引了用户的注意,并且显得很重要。对于排版,明显不同的尺寸缩放比例可以帮助建立内容层次结构,并使内容扫描变得轻松而轻松。

  • 辅助视觉层次结构的另一种方法是通过设计一致性和例外。一致对齐,外观相似,重复或相邻的元素给人的印象是它们是相关且同等重要的,而偏离元素以及不寻常的形状和有趣的纹理或样式将更加显着。太多的设计例外会引起人们的关注,并会增加复杂性,因此,谨慎使用它们是一个好习惯。


专家提示:研究格式塔原理及其在UI设计中的应用将有助于我们理解视觉感知和分组以改善视觉层次。


色彩应用

颜色不应该是传达信息或增加意义的唯一方法,但它仍然有用且很有影响力,因此不应将其视为装饰性元素。颜色具有含义,尽管没有硬性规定,但是太多的颜色会导致信息疲劳,并且不一致地使用颜色会导致混乱。

  • 避免使用太多颜色。通常,三种颜色足以描述页面的所有重要视觉元素。60–30–10规则可以帮助我们建立完美的和谐。其中60%的彩色项目由原色组成,以创建统一的产品主题,具有30%的辅助颜色增强含义和/或创建引人注目的效果,以及10%的强调色,以补充和辅助主颜色和辅助颜色。

  • 此外,我们需要确保为消息使用正确的色调。除了美学,颜色还可以创造情感和无意识的联系。特定阴影的含义会因我们所处的文化和环境而异,并且颜色通常具有不同的含义-在西方世界,错误是红色,成功是绿色,信息是蓝色等。


专家提示:可以将我们自己的含义分配给颜色,只要它们不与既定规范重叠,并且我们在整个产品中使它们保持一致。


符号学

符号学是对符号/图标及其含义的研究。它着重于人们如何形成和解释这些含义,这取决于人们所看到的上下文。在用户界面中,图标是可视语言的一部分,用于表示功能,功能或内容。符号学可以帮助我们设计立即被识别和理解的图像。

  • 尽管这些年来,我们已经开发出具有大多数人接受和理解的含义的图标。用户还习惯于使用特定于平台的图标,并且可以轻松地进行解释。在可能的情况下,最好遵循这些既定的解决方案,以获得熟悉和流畅的体验。

  • 当然,在某些情况下,我们需要设计具有特定功能的自定义产品特定图标。这些图标必须尽可能简单明了,以确保清晰度。它们还应该具有一致的视觉样式,以传达其功能或与其他非功能性元素区分开。

  • 最后,我们不应该仅仅依靠视觉隐喻来传达含义,因为某些关联可能并不那么明显。如果图标需要标题来描述其含义,则可能不合适。如果不确定,请与实际用户一起测试我们的设计会有所帮助。


专家提示:图标不仅易于解释,而且还可以具有多种含义。因此,将标记与功能图标结合使用是一种很好的做法。


2.互动

记忆

许多心理学实验表明,健康个体的处理能力非常有限。在我们的短期记忆中,我们大多数人平均可以保留7项,具体取决于个人。我们的大脑并未针对数字产品所需的抽象思维和数据记忆进行优化,因此良好的设计会有所作为。


  • 减少页面上可用选项和信息的数量,以及使用清晰的标题,面包屑和“后退”选项来访问以前的内容,将帮助用户记住或提醒自己他们在哪里,打算做什么或要做什么。是必需的。

  • 交互元素上或附近的清晰可见副本将帮助用户在整个交互过程中保持知情和自信。例如,表单标签应始终可见,动作不应隐藏在悬停后面,按钮应提供目标位置的上下文,并且各节的标题应明确。


专家提示:通过称为“块”的过程可以增加我们的短期记忆和处理能力。这是我们在视觉上将项目分组以形成更容易记住的较大项目的地方。


改善运动体验


菲茨法

菲茨法则为人类的运动和互动提供了一个模型。它指出,将指针(光标或手指)快速移动到目标区域所需的时间是其距目标的距离除以目标大小的函数。意味着较小的目标会增加互动时间。

 

根据Fitts法则,我们旨在减小用户与目标之间的距离,同时增加其尺寸。该法律主要适用于导航和按钮。菜单和子菜单元素应在附近,而按钮,链接和分页应在较大区域上单击,以实现更快更准确的交互。


专家提示:根据可用性最佳实践,按钮/链接的最小尺寸为42x42像素(重击尺寸)。


奖励:提高绩效

到目前为止,我们已经建立了包容性设计,旨在让尽可能多的人访问并实现他们的目标或解决他们的问题,尽管他们有自己的情况。我们可能很幸运,可以使用进的设备或超高速互联网,但是当我们的信号不太好时,我们会感到挣扎。对于大多数人来说,老式设备和糟糕的互联网已成为常态,因此,为获得最佳性能而设计是一件大事。

  • 极简主义是关键。如果我们打算创造一种可以被尽可能多的人使用的产品,那么我们就应该摆脱不必要的一切。图形,图像或动画是有价值的,还是增加了视觉噪音和加载时间?如果是的话,那就必须走了。

  • 图像优化是帮助提高数字产品性能的另一个标准。通过将图像调整为合适的大小,然后通过诸如ImageOptim和TinyPNG之类的工具运行它们,可以节省宝贵的千字节和实际的加载时间。

  • 开发人员通常使用的一种提高性能的技术是“延迟加载”模式,其中图像的加载是异步的,并延迟到需要时才加载。例如,如果您快速滚动到页面底部,则在网站完全加载之前,您可能会看到类似网站线框的内容。“渐进图像加载”的一种替代方法是“渐进图像加载”,其中我们显示一个空的占位符框<div>,然后用小的低质量模糊图像填充它,最后用所需的高质量图像替换它。

在每个数字产品中都遵循上述最佳实践,这是高可访问性标准的良好起点。但是总会有改进的余地,并且更好地了解我们的用户可以揭示提高无障碍标准的新方法。因此,有必要花费一些时间和金钱来更多地了解我们的不同类型的用户,因为他们可以教会我们很多有关使包容性体验成为现实的知识。


了解我们的用户将帮助我们练习同理心。“赋权”不是偶然的设计思维过程的第一步。在移情阶段,我们的目标是加深对我们正在设计的人员及其独特视角的了解,因此我们可以在进行任何设计决策时与他们认同并代表他们。

zhuanz

JavaScript必须掌握的基础 ---> this

seo达人

this

this是我们在书写代码时最常用的关键词之一,即使如此,它也是JavaScript最容易被最头疼的关键词。那么this到底是什么呢?


如果你了解执行上下文,那么你就会知道,其实this是执行上下文对象的一个属性:


executionContext = {

   scopeChain:[ ... ],

   VO:{

       ...

   },

   this:  ?

}

执行上下文中有三个重要的属性,作用域链(scopeChain)、变量对象(VO)和this。


this是在进入执行上下文时确定的,也就是在函数执行时才确定,并且在运行期间不允许修改并且是永久不变的


在全局代码中的this

在全局代码中this 是不变的,this始终是全局对象本身。


var a = 10;

this.b = 20;

window.c = 30;


console.log(this.a);

console.log(b);

console.log(this.c);


console.log(this === window) // true

// 由于this就是全局对象window,所以上述 a ,b ,c 都相当于在全局对象上添加相应的属性

如果我们在代码运行期尝试修改this的值,就会抛出错误:


this = { a : 1 } ; // Uncaught SyntaxError: Invalid left-hand side in assignment

console.log(this === window) // true

函数代码中的this

在函数代码中使用this,才是令我们最容易困惑的,这里我们主要是对函数代码中的this进行分析。


我们在上面说过this的值是,进入当前执行上下文时确定的,也就是在函数执行时并且是执行前确定的。但是同一个函数,作用域中的this指向可能完全不同,但是不管怎样,函数在运行时的this的指向是不变的,而且不能被赋值。


function foo() {

   console.log(this);

}


foo();  // window

var obj={

   a: 1,

   bar: foo,

}

obj.bar(); // obj

函数中this的指向丰富的多,它可以是全局对象、当前对象、或者是任意对象,当然这取决于函数的调用方式。在JavaScript中函数的调用方式有一下几种方式:作为函数调用、作为对象属性调用、作为构造函数调用、使用apply或call调用。下面我们将按照这几种调用方式一一讨论this的含义。


作为函数调用

什么是作为函数调用:就是独立的函数调用,不加任何修饰符。


function foo(){

   console.log(this === window); // true

   this.a = 1;

   console.log(b); // 2

}

var b = 2;

foo();

console.log(a); // 1

上述代码中this绑定到了全局对象window。this.a相当于在全局对象上添加一个属性 a 。


在严格模式下,独立函数调用,this的绑定不再是window,而是undefined。


function foo() {

   "use strict";

   console.log(this===window); // false

   console.log(this===undefined); // true

}

foo();

这里要注意,如果函数调用在严格模式下,而内部代码执行在非严格模式下,this 还是会默认绑定为 window。


function foo() {

   console.log(this===window); // true

}



(function() {

   "use strict";

   foo();

})()

对于在函数内部的函数独立调用 this 又指向了谁呢?


function foo() {

   function bar() {

       this.a=1;

       console.log(this===window); // true

   }

   bar()

}

foo();

console.log(a); // 1

上述代码中,在函数内部的函数独立调用,此时this还是被绑定到了window。


总结:当函数作为独立函数被调用时,内部this被默认绑定为(指向)全局对象window,但是在严格模式下会有区别,在严格模式下this被绑定为undefined。


作为对象属性调用

var a=1;

var obj={

   a: 2,

   foo: function() {

       console.log(this===obj); // true

       console.log(this.a); // 2

   }

}

obj.foo();

上述代码中 foo属性的值为一个函数。这里称 foo 为 对象obj 的方法。foo的调用方式为 对象 . 方法 调用。此时 this 被绑定到当前调用方法的对象。在这里为 obj 对象。


再看一个例子:


var a=1;

var obj={

   a: 2,

   bar: {

       a: 3,

       foo: function() {

           console.log(this===bar); // true

           console.log(this.a); // 3

       }

   }

}

obj.bar.foo();

遵循上面说的规则 对象 . 属性 。这里的对象为 obj.bar 。此时 foo 内部this被绑定到了 obj.bar 。 因此 this.a 即为 obj.bar.a 。


再来看一个例子:


var a=1;

var obj={

   a: 2,

   foo: function() {

       console.log(this===obj); // false

       console.log(this===window); // true

       console.log(this.a); // 1

   }

}


var baz=obj.foo;

baz();

这里 foo 函数虽然作为对象obj 的方法。但是它被赋值给变量 baz 。当baz调用时,相当于 foo 函数独立调用,因此内部 this被绑定到 window。


使用apply或call调用

apply和call为函数原型上的方法。它可以更改函数内部this的指向。


var a=1;

function foo() {

   console.log(this.a);

}

var obj1={

   a: 2

}

var obj2={

   a: 3

}

var obj3={

   a: 4

}

var bar=foo.bind(obj1);

bar();// 2  this => obj1

foo(); // 1  this => window

foo.call(obj2); // 3  this => obj2

foo.call(obj3); // 4  this => obj3

当函数foo 作为独立函数调用时,this被绑定到了全局对象window,当使用bind、call或者apply方法调用时,this 被分别绑定到了不同的对象。


作为构造函数调用

var a=1;

function Person() {

   this.a=2;  // this => p;

}

var p=new Person();

console.log(p.a); // 2

上述代码中,构造函数 Person 内部的 this 被绑定为 Person的一个实例。


总结:


当我们要判断当前函数内部的this绑定,可以依照下面的原则:


函数是否在是通过 new 操作符调用?如果是,this 绑定为新创建的对象

var bar = new foo();     // this => bar;

函数是否通过call或者apply调用?如果是,this 绑定为指定的对象

foo.call(obj1);  // this => obj1;

foo.apply(obj2);  // this => obj2;

函数是否通过 对象 . 方法调用?如果是,this 绑定为当前对象

obj.foo(); // this => obj;

函数是否独立调用?如果是,this 绑定为全局对象。

foo(); // this => window

DOM事件处理函数中的this

1). 事件绑定


<button id="btn">点击我</button>


// 事件绑定


function handleClick(e) {

   console.log(this); // <button id="btn">点击我</button>

}

       document.getElementById('btn').addEventListener('click',handleClick,false);  //   <button id="btn">点击我</button>

       

document.getElementById('btn').onclick= handleClick; //  <button id="btn">点击我</button>

根据上述代码我们可以得出:当通过事件绑定来给DOM元素添加事件,事件将被绑定为当前DOM对象。


2).内联事件


<button onclick="handleClick()" id="btn1">点击我</button>

<button onclick="console.log(this)" id="btn2">点击我</button>


function handleClick(e) {

   console.log(this); // window

}


//第二个 button 打印的是   <button id="btn">点击我</button>

我认为内联事件可以这样理解:


//伪代码


<button onclick=function(){  handleClick() } id="btn1">点击我</button>

<button onclick=function() { console.log(this) } id="btn2">点击我</button>

这样我们就能理解上述代码中为什么内联事件一个指向window,一个指向当前DOM元素。(当然浏览器处理内联事件时并不是这样的)


定时器中的this

定时器中的 this 指向哪里呢?


function foo() {

   setTimeout(function() {

       console.log(this); // window

   },1000)

}

foo();  

再来看一个例子


var name="chen";

var obj={

   name: "erdong",

   foo: function() {

       console.log(this.name); // erdong

       setTimeout(function() {

           console.log(this.name); // chen

       },1000)

   }

}

obj.foo();

到这里我们可以看到,函数 foo 内部this指向为调用它的对象,即:obj 。定时器中的this指向为 window。那么有什么办法让定时器中的this跟包裹它的函数绑定为同一个对象呢?


1). 利用闭包:


var name="chen";

var obj={

   name: "erdong",

   foo: function() {

       console.log(this.name) // erdong

       var that=this;

       setTimeout(function() {

           // that => obj

           console.log(that.name); // erdong

       },1000)

   }

}

obj.foo();

利用闭包的特性,函数内部的函数可以访问含义访问当前词法作用域中的变量,此时定时器中的 that 即为包裹它的函数中的 this 绑定的对象。在下面我们会介绍利用 ES6的箭头函数实现这一功能。


当然这里也可以适用bind来实现:


var name="chen";

var obj={

   name: "erdong",

   foo: function() {

       console.log(this.name); // erdong

       setTimeout(function() {

           // this => obj

           console.log(this.name); // erdong

       }.bind(this),1000)

   }

}

obj.foo();

被忽略的this

如果你把 null 或者 undefined 作为 this 的绑定对象传入 call 、apply或者bind,这些值在调用时会被忽略,实例 this 被绑定为对应上述规则。


var a=1;

function foo() {

   console.log(this.a); // 1  this => window

}

var obj={

   a: 2

}

foo.call(null);

var a=1;

function foo() {

   console.log(this.a); // 1  this => window

}

var obj={

   a: 2

}

foo.apply(null);

var a=1;

function foo() {

   console.log(this.a); // 1  this => window

}

var obj={

   a: 2

}

var bar = foo.bind(null);

bar();

bind 也可以实现函数柯里化:


function foo(a,b) {

   console.log(a,b); // 2  3

}

var bar=foo.bind(null,2);

bar(3);

更复杂的例子:


var foo={

   bar: function() {

       console.log(this);

   }

};


foo.bar(); // foo

(foo.bar)(); // foo


(foo.bar=foo.bar)(); // window

(false||foo.bar)();  // window

(foo.bar,foo.bar)();  // window

上述代码中:


foo.bar()为对象的方法调用,因此 this 绑定为 foo 对象。


(foo.bar)() 前一个() 中的内容不计算,因此还是 foo.bar()


(foo.bar=foo.bar)() 前一个 () 中的内容计算后为 function() { console.log(this); } 所以这里为匿名函数自执行,因此 this 绑定为 全局对象 window


后面两个实例同上。


这样理解会比较好:


(foo.bar=foo.bar)  括号中的表达式执行为 先计算,再赋值,再返回值。

(false||foo.bar)()    括号中的表达式执行为 判断前者是否为 true ,若为true,不计算后者,若为false,计算后者并返回后者的值。

(foo.bar,foo.bar)   括号中的表达式之行为分别计算 “,” 操作符两边,然后返回  “,” 操作符后面的值。

箭头函数中的this

箭头函数时ES6新增的语法。


有两个作用:


更简洁的函数

本身不绑定this

代码格式为:


// 普通函数

function foo(a){

   // ......

}

//箭头函数

var foo = a => {

   // ......

}


//如果没有参数或者参数为多个


var foo = (a,b,c,d) => {

   // ......

}

我们在使用普通函数之前对于函数的this绑定,需要根据这个函数如何被调用来确定其内部this的绑定对象。而且常常因为调用链的数量或者是找不到其真正的调用者对 this 的指向模糊不清。在箭头函数出现后其内部的 this 指向不需要再依靠调用的方式来确定。


箭头函数有几个特点(与普通函数的区别)


箭头函数不绑定 this 。它只会从作用域链的上一层继承 this。

箭头函数不绑定arguments,使用reset参数来获取实参的数量。

箭头函数是匿名函数,不能作为构造函数。

箭头函数没有prototype属性。

不能使用 yield 关键字,因此箭头函数不能作为函数生成器。

这里我们只讨论箭头函数中的this绑定。


用一个例子来对比普通函数与箭头函数中的this绑定:


var obj={

   foo: function() {

       console.log(this); // obj

   },

   bar: () => {

       console.log(this); // window

   }

}

obj.foo();

obj.bar();

上述代码中,同样是通过对象 . 方法调用一个函数,但是函数内部this绑定确是不同,只因一个数普通函数一个是箭头函数。


用一句话来总结箭头函数中的this绑定:


个人上面说的它会从作用域链的上一层继承 this ,说法并不是很正确。作用域中存放的是这个函数当前执行上下文与所有父级执行上下文的变量对象的集合。因此在作用域链中并不存在 this 。应该说是作用域链上一层对应的执行上下文中继承 this 。


箭头函数中的this继承于作用域链上一层对应的执行上下文中的this


var obj={

   foo: function() {

       console.log(this); // obj

   },

   bar: () => {

       console.log(this); // window

   }

}

obj.bar();

上述代码中obj.bar执行时的作用域链为:


scopeChain = [

   obj.bar.AO,

   global.VO

]

根据上面的规则,此时bar函数中的this指向为全局执行上下文中的this,即:window。


再来看一个例子:


var obj={

   foo: function() {

       console.log(this); // obj

       var bar=() => {

           console.log(this); // obj

       }

       bar();

   }

}

obj.foo();

在普通函数中,bar 执行时内部this被绑定为全局对象,因为它是作为独立函数调用。但是在箭头函数中呢,它却绑定为 obj 。跟父级函数中的 this 绑定为同一对象。


此时它的作用域链为:


scopeChain = [

    bar.AO,

    obj.foo.AO,

    global.VO

]

这个时候我们就差不多知道了箭头函数中的this绑定。


继续看例子:


var obj={

   foo: () => {

       console.log(this); // window

       var bar=() => {

           console.log(this); // window

       }

       bar();

   }

}

obj.foo();

这个时候怎么又指向了window了呢?


我们还看当 bar 执行时的作用域链:


scopeChain = [

    bar.AO,

    obj.foo.AO,

    global.VO

]

当我们找bar函数中的this绑定时,就会去找foo函数中的this绑定。因为它是继承于它的。这时 foo 函数也是箭头函数,此时foo中的this绑定为window而不是调用它的obj对象。因此 bar函数中的this绑定也为全局对象window。


我们在回头看上面关于定时器中的this的例子:


var name="chen";

var obj={

   name: "erdong",

   foo: function() {

       console.log(this.name); // erdong

       setTimeout(function() {

           console.log(this); // chen

       },1000)

   }

}

obj.foo();

这时我们就可以很简单的让定时器中的this与foo中的this绑定为同一对象:


var name="chen";

var obj={

   name: "erdong",

   foo: function() {

       // this => obj

       console.log(this.name); // erdong

       setTimeout(() =>  {

           // this => foo中的this => obj

           console.log(this.name); // erdong

       },1000)

   }

}

obj.foo();

5G将开启全新的出行方式

分享达人

5G以其更快的速度、连接和云访问,将大数据引进车内





在MWC上,华为、小米、三星等通讯企业纷纷发布了5G手机,而吉利也在2月26日的MWC上,宣布了与高通和高新兴合作发布吉利全球首批支持5G和C-V2X的量产车型计划。可见5G时代对于各大车企来说有着巨大的影响,尤其是车联网产业。那么,在5G环境下,汽车行业究竟有什么变化呢?



5G是一代的移动网络,凭借高带宽和低延迟,提供了更快的速度、连接和云访问。5G的最大速度可达到每秒20GB,比4G要快100倍。它可以应用于手机、无人驾驶、VR、电影、充电桩、医疗、农业等多个领域。而基于5G通讯技术推出的C-V2X,是实现无人驾驶和车内技术的重要前提。



万物互联


万物互联是自动驾驶汽车发展的关键,基于5G通讯技术推出的C-V2X能让联网车辆与交通基础设施进行通信。通过5G可以实现自动驾驶汽车彼此之间所有数据的沟通与互联。并与交通灯、道路、传感器、停车场等基础设施之间的信息互联,最终实现车路协同、万物互联。



车车互联提升驾驶辅助


在5G环境下的汽车,可以通过云计算来计算车与车之间的距离、车辆的下一步动作、隐藏车辆可视化、零误差高清导航等信息。同时,也可以与其他车辆实现共享数据,提升ADAS和AEB等驾驶辅助功能,来避免车辆之间发生碰撞。



智慧交通引领场景化设计


5G环境下,大量的数据将被引入车内,来提供更准确的数据信息。如,高速收费、红绿灯、RTTI、实时车位情况、消费支付、行人检测等情况。众多的交互有助于车辆更好的了解环境信息,并作出反馈,从而提供更好的场景化设计。如,自动超车、协作式避让、自动播报前方道路拥挤程度并重新规划路线功能等场景化设计。



再比如,哈曼正在研发的交通信号灯速度优化建议技术,帮助司机根据红绿灯信息调整时速;西亚特测试了在交通信号灯中安装热像仪,以检测行人的动作并将数据反馈给汽车。



车、商业、家居互联改善驾驶体验


当车辆与酒店、商场、影院、餐厅、健身房、加油站、家居、充电桩等场所相连接,就需要以5g结合C-V2X技术的部署为基准。从而根据车主的需求,更快地预定房间、订餐、定电影票、充电桩、商场优惠等活动,实现终端之间更的通信。



比如,我们要去看电影,那么车辆会根据实时路况(是否拥堵、有几个红绿灯、是否有车祸)为您选择最佳的路线方案,并通知停车场到达时间,以方便确定是否有符合您时间的停车位,从而得到及时的反馈。




车载娱乐


可以说,如今我们所说的无人驾驶、车载触屏、全息投影、AR(增强现实)、VR游戏、AR-HUD、实时电影、车辆之间影片共享、移动办公、多模态交互等车载信息娱乐,都是需要在5G环境下来完成的。在未来,你可以在车内利用几秒的时间下载一部电影,也可以在车内与其他车辆之间建立网络游戏通信,实现虚拟内容与车辆运动的实时对接。



奔驰与哈曼合作开发的MBUX信息娱乐平台也引入了增强现实(AR)的车载导航系统。


MBUX信息AR车载导航系统

BMW Vision Next 100 AR-HUD


奥迪发布的“沉浸式车内娱乐系统”,让乘客在车内佩戴VR眼镜,然后车辆会根据行驶路线路况,实时匹配逼真的电竞类影片。


奥迪发布的“沉浸式车内娱乐系统”

手势交互

车载机器人

全息投影

移动办公


安全驾驶


未来的汽车将变得更加安全和,因为5G凭借其更高的带宽、最小的延迟和零误差高清导航,能够预防事故和观察到车身周围的各个角落。再结合C-V2X技术将极大地促进车辆之间、车辆与行人、道路基础设施之间的信息流动和监控车辆异常情况。从而做到提前预知危险,并迅速作出响应来提高道路安全性。另外,5G对于阻止黑客攻击和数据拦截能够得到更快响应,从而保障通讯安全。



想象一下,如果你的车可以在1毫秒内做出反应并将反馈传达给数百人,那么,危险系数就会降低很多。5G时代,端到端的时长为1毫秒,同时1平方公里内可同时连接100万个网络,足以满足智能交通和自动驾驶的要求。



无人驾驶


5G是实现无人驾驶最为关键的因素,如今,5G的成功研发有助于车辆之间大量的数据传输和存储,实现车联网的同时,也保障了车辆行驶的安全性;另一方面,5G可实现数据更快速的云访问,从而更有效地减少传感器技术的成本,最终实现无人驾驶。


总结


5G以其更快的速度、连接和云访问,将大数据引进车内。从而,在提升驾驶体验的同时,实现了汽车与万物的互联,保障了汽车驾驶中的安全性,从而为无人驾驶汽车提供了技术支撑和更便捷和的信息娱乐系统。


  • 5G将成为未来十年全球主导的移动通信标准;

  • 基于5G的C-V2X技术,会加快无人驾驶技术的落地;

  • 5G环境下的车载信息娱乐系统将改善用户驾驶体验,同时催生大量的市场新机会;

  • 共享平台下,5G将根据实时路况作出更准确的判断,从而减少时间成本和停车成本;

  • 5G远不止应用于汽车、手机,它将应用于医疗、机器人、农作业、航空等更多方面;

  • 5G技术为无人驾驶的实现提供了技术支撑和安全保障,同时也降低了安装传感器技术的成本;

  • 5G实现了车与车、道路、家居、商业之间真正意义上的“车联网”,为交通提供更准确的交通信息和通信信息;

  • 5G将会提供更多的场景,从而加快市场社会变革和新的商机,但需要大量的资金投入,能否将技术化为盈利是一大问题;

  • 5G以毫秒的速度,提供更精细的地图数据和更高级的驾驶辅助,并作出快速的反馈,提高了驾驶的安全性,对于减少交通拥堵,提高交通效率和道路安全有着积极的作用。



转自-站酷

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计  cs界面设计  ipad界面设计  包装设计  图标定制  用户体验 、交互设计、 网站建设 平面设计服务




Three.js 基础入门

前端达人

课程介绍

近些年,浏览器的功能越来越强大,渐渐得成为了复杂应用和图形的平台。同时,现有大多数浏览器实现了对 WebGL 的支持,但要直接使用 WebGL 相关接口进行开发,则需要学习复杂的着色器语言,且开发周期长,不利于项目的快速开发。

面对这种情况,Three.js 应运而生,它不但对 WebGL 进行了封装,将复杂的接口简单化,而且基于面向对象思维,将数据结构对象化,非常方便我们开发。Three.js 的发展十分迅速,然而配套的学习材料却比较匮乏,于是便有了当前的这个课程。

本课程作为入门课程,不会深入做源码解析,主要协助初学者了解 Three.js 的数据结构,基础 API 以及相关辅助插件的使用。帮助初学者达到快速入门的目的。

本课程共包含四大部分。

第一部分(第01-02课),入门前概述,带你初步认识 Three.js、框架选择标准、开发工具,源码获取,实现一个“Hello World”辅助工具。
第二部分(第03-08课),基础功能篇,主要包括 Object3D、Scene、Mesh、Group、Geometry、Materials、Lights、Cameras、粒子等相关功能的介绍。
第三部分(第09-15课),进阶篇,主要包括 Controls、Loaders、Animation、Tween、核心对象,与场景之间的交互以及性能优化介绍。
第四部分(第16课),实战篇,带大家利用所学知识实现一个 3D 小案例。

作者简介

郑世强,现就职于上海某网络公司担任前端工程师,CSDN 博客作者,长期活跃于各大论坛,擅长前端开发、WEBGL 开发。

课程内容

第01课:入门前准备

什么是 WebGL?

WebGL(Web 图形库)是一种 JavaScript API,用于在任何兼容的 Web 浏览器中呈现交互式 3D 和 2D 图形,而无需使用插件。WebGL 通过引入一个与 OpenGL ES 2.0 紧密相符合的 API,可以在 HTML5 <canvas> 元素中使用(简介引自 MDN)。

以我的理解,WebGL 给我们提供了一系列的图形接口,能够让我们通过 JavaScript 去使用 GPU 来进行浏览器图形渲染的工具。


什么是 Three.js?

Three.js 是一款 webGL 框架,由于其易用性被广泛应用。Three.js 在 WebGL 的 API 接口基础上,又进行的一层封装。它是由居住在西班牙巴塞罗那的程序员 Ricardo Cabbello Miguel 所开发,他更为人知的网名是 Mr.doob。



Three.js 以简单、直观的方式封装了 3D 图形编程中常用的对象。Three.js 在开发中使用了很多图形引擎的高级技巧,极大地提高了性能。另外,由于内置了很多常用对象和极易上手的工具,Three.js 的功能也非常强大。最后,Three.js 还是完全开源的,你可以在 GitHub 上找到它的源代码,并且有很多人贡献代码,帮助 Mr.doob 一起维护这个框架。

WEBGL 和 Three.js 的关系

WebGL 原生 API 是一种非常低级的接口,而且还需要一些数学和图形学的相关技术。对于没有相关基础的人来说,入门真的很难,Three.js 将入门的门槛降低了一大截,对 WebGL 进行封装,简化我们创建三维动画场景的过程。只要你有一定的 JavaScript 基础,有一定的前端经验,我坚信,用不了多长时间,三维制作会变得很简单。



用最简单的一句话概括:WebGL 和 Three.js 的关系,相当于 JavaScript 和 jQuery 的关系。

功能概述

Three.js 作为 WebGL 框架中的佼佼者,由于它的易用性和扩展性,使得它能够满足大部分的开发需求,Three.js 的具体功能如下:


Three.js 掩盖了 3D 渲染的细节:Three.js 将 WebGL 原生 API 的细节抽象化,将 3D 场景拆解为网格、材质和光源(即它内置了图形编程常用的一些对象种类)。
面向对象:开发者可以使用上层的 JavaScript 对象,而不是仅仅调用 JavaScript 函数。
功能非常丰富:Three.js 除封装了 WebGL 原始 API 之外,Three.js 还包含了许多实用的内置对象,可以方便地应用于游戏开发、动画制作、幻灯片制作、髙分辨率模型和一些特殊的视觉效果制作。
速度很快:Three.js 采用了 3D 图形最佳实践来保证在不失可用性的前提下,保持极高的性能。
支持交互:WebGL 本身并不提供拾取(Picking)功能(即是否知道鼠标正处于某个物体上)。而 Three.js 则固化了拾取支持,这就使得你可以轻松为你的应用添加交互功能。
包含数学库:Three.js 拥有一个强大易用的数学库,你可以在其中进行矩阵、投影和矢量运算。
内置文件格式支持:你可以使用流行的 3D 建模软件导出文本格式的文件,然后使用 Three.js 加载,也可以使用 Three.js 自己的 JSON 格式或二进制格式。
扩展性很强:为 Three.js 添加新的特性或进行自定义优化是很容易的事情。如果你需要某个特殊的数据结构,那么只需要封装到 Three.js 即可。
支持HTML5 Canvas:Three.js 不但支持 WebGL,而且还支持使用 Canvas2D、Css3D 和 SVG 进行渲染。在未兼容 WebGL 的环境中可以回退到其它的解决方案。


缺点

虽然 Three.js 的优势很大,但是它也有它的不足之处:



官网文档非常粗糙,对于新手极度不友好。

国内的相关资源匮乏。

Three.js 所有的资料都是以英文格式存在,对国内的朋友来说又提高了门槛。

Three.js 不是游戏引擎,一些游戏相关的功能没有封装在里面,如果需要相关的功能需要进行二次开发。


Three.js 与其他库的对比

随着 WebGL 的迅速发展,相关的 WebGL 库也丰富起来,接下来介绍几个比较火的 WebGL 库。



与 Babylon.js 对比

Babylon.JS 是最好的 JavaScript 3D 游戏引擎,它能创建专业级三维游戏。主要以游戏开发和易用性为主。与 Three.js 之间的对比:



Three.js 比较全面,而 Babylon.js 专注于游戏方面。

Babylon.js 提供了对碰撞检测、场景重力、面向游戏的照相机,Three.js 本身不自带,需要依靠引入插件实现。

对于 WebGL 的封装,双方做得各有千秋,Three.js 浅一些,好处是易于扩展,易于向更底层学习;Babylon.js 深一些,好处是易用扩展难度大一些。

Three.js 的发展依靠社区推动,出来的比较早,发展比较成熟,Babylon.js 由微软公司在2013推出,文档和社区都比较健全,国内还不怎么火。


与 PlayCanvas 对比

PlayCanvas 是一个基于 WebGL 游戏引擎的企业级开源 JavaScript 框架,它有许多的开发工具能帮你快速创建 3D 游戏。与 Three.js 之间的对比:



PlayCanvas 的优势在于它有云端的在线可视化编辑工具。

PlayCanvas 的扩展性不如 Three.js。

最主要是 PlayCanvas 不完全开源,还商业付费。


与 Cesium 对比

Cesium 是国外一个基于 JavaScript 编写的使用 WebGL 的地图引擎,支持 3D、2D、2.5D 形式的地图展示,可以自行绘制图形,高亮区域。与 Three.js 对比:



Cesium 是一个地图引擎,专注于 Gis,相关项目推荐使用它,其它项目还是算了。

至于库的扩展,其它的配套插件,以及周边的资源都不及Three.js。


总结

通过以上信息我们发现,Three.js 在其库的扩展性,易用性以及功能方面有很好的优势。学习 Three.js 入门 3D 开发不但门槛低,而且学习曲线不会太陡,即使以后转向 WebGL 原生开发,也能通过 Three.js 学习到很多有用的知识。



现在最火的微信小游戏跳一跳也是在 Three.js 的基础上开发出来的。所以,Three.js 是我们必须要学的 WebGL 框架。


入门前准备

浏览器兼容


Three.js 可以使用 WebGL 在所有现代浏览器上渲染场景。对于旧版浏览器,尤其是 Internet Explorer 10 及更低版本,您可能需要回退到其他渲染器(CSS2DRenderer、CSS3DRenderer、SVGRenderer、CanvasRenderer)。



注意:如果您不需要支持这些旧版浏览器,则不推荐使用其他渲染器,因为它们速度较慢并且支持的功能比 WebGLRenderer 更少。


点击查看原图

即可下载当前版本的代码及相关案例,文件下载解压后是这样的:


微信截图_20200529213036.png

其中相关文件夹的内容是:

build:里面含有 Three.js 构建出来的 JavaScript 文件,可以直接引入使用,并有压缩版;
docs:Three.js 的官方文档;
editor:Three.js 的一个网页版的模型编辑器;
examples:Three.js 的官方案例,如果全都学会,必将成为大神;
src:这里面放置的全是编译 Three.js 的源文件;
test:一些官方测试代码,我们一般用不到;
utils:一些相关插件;
其他:开发环境搭建、开发所需要的文件,如果不对 Three.js 进行二次开发,用不到。
还有第三种,就是直接去 GitHub 上下载源码,和在官网上下载的代码一样。

hello World

前面说了这么多,准备了这么多,最后,放上我们的第一个案例吧。由此来打开学习 Three.js 的大门:


<!DOCTYPE html><html><head>    <meta charset=utf-8>    <title>我的第一个Three.js案例</title>    <style>        body {            margin: 0;        }        canvas {            width: 100%;            height: 100%;            display: block;        }    </style></head><body onload="init()"><script src="https://cdn.bootcss.com/three.js/92/three.js"></script><script>    //声明一些全局变量    var renderer, camera, scene, geometry, material, mesh;    //初始化渲染器    function initRenderer() {        renderer = new THREE.WebGLRenderer(); //实例化渲染器        renderer.setSize(window.innerWidth, window.innerHeight); //设置宽和高        document.body.appendChild(renderer.domElement); //添加到dom    }    //初始化场景    function initScene() {        scene = new THREE.Scene(); //实例化场景    }    //初始化相机    function initCamera() {        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); //实例化相机        camera.position.set(0, 0, 15);    }    //创建模型    function initMesh() {        geometry = new THREE.BoxGeometry( 2, 2, 2 ); //创建几何体        material = new THREE.MeshNormalMaterial(); //创建材质        mesh = new THREE.Mesh( geometry, material ); //创建网格        scene.add( mesh ); //将网格添加到场景    }    //运行动画    function animate() {        requestAnimationFrame(animate); //循环调用函数        mesh.rotation.x += 0.01; //每帧网格模型的沿x轴旋转0.01弧度        mesh.rotation.y += 0.02; //每帧网格模型的沿y轴旋转0.02弧度        renderer.render( scene, camera ); //渲染界面    }    //初始化函数,页面加载完成是调用    function init() {        initRenderer();        initScene();        initCamera();        initMesh();        animate();    }</script></body></html>

请将上面的代码复制到 HTML 文件中,然后使用浏览器打开,我们就会发现下面的效果:

点击查看原图



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

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

原文链接:https://blog.csdn.net/valada/java/article/details/80871701







JavaScript的padStart()和padEnd()格式化字符串使用技巧

seo达人

用例

让我们从介绍几种不同的填充用例开始。


标签和值

假设你在同一行上有标签和值,例如 name:zhangsan 和 Phone Number:(555)-555-1234。如果把他们放在一起看起来会有点奇怪,会是这样:


Name: zhangsan

Phone Number: (555)-555-1234

你可能想要这个。


Name:           zhangsan

Phone Number:   (555)555-1234

或这个...


       Name: zhangsan

Phone Number: (555)555-1234

金额

在中国,显示价格时通常显示两位数的角、分。所以代替这个...


¥10.1

你会想要这个。


¥10.01

日期

对于日期,日期和月份都需要2位数字。所以代替这个...


2020-5-4

你会想要这个。


2020-05-04

时间

与上面的日期类似,对于计时器,你需要2位数字表示秒,3位数字表示毫秒。所以代替这个...


1:1

你会想要这个。


01:001

padstart()

让我们从 padStart() 以及标签和值示例开始。假设我们希望标签彼此正确对齐,以使值在同一位置开始。


       Name: zhangsan

Phone Number: (555)555-1234

由于 Phone Number 是两个标签中较长的一个,因此我们要在 Name 标签的开头加上空格。为了将来的需要,我们不要把它专门填充到电话号码的长度,我们把它填充到长一点,比如说20个字符。这样一来,如果你在未来使用较长的标签,这一招仍然有效。


在填充之前,这是用于显示此信息的入门代码。


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log(label1 + ": " + name);

console.log(label2 + ": " + phoneNumber);


//Name: zhangsan

//Phone Number: (555)-555-1234

现在,让我们填充第一个标签。要调用 padStart(),你需要传递两个参数:一个用于填充字符串的目标长度,另一个用于你希望填充的字符。在这种情况下,我们希望长度为20,而填充字符为空格。


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log(label1.padStart(20, " ") + ": " + name);

console.log(label2 + ": " + phoneNumber);


//               Name: zhangsan

////Phone Number: (555)-555-1234

现在填充第二行。


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log(label1.padStart(20, " ") + ": " + name);

console.log(label2.padStart(20, " ") + ": " + phoneNumber);


//               Name: zhangsan

////     Phone Number: (555)-555-1234

padEnd()

对于相同的标签和值示例,让我们更改填充标签的方式。让我们将标签向左对齐,以便在末尾添加填充。


初始代码


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log(label1 + ": " + name);

console.log(label2 + ": " + phoneNumber);


//Name: zhangsan

//Phone Number: (555)-555-1234

现在,让我们填充第一个标签,与我们之前所做的类似,但有两个小区别。现在,我们使用 padEnd() 而不是padStart(),并且需要在填充之前将冒号与标签连接起来,这样我们就能确保冒号在正确的位置。


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log((label1 + ': ').padEnd(20, ' ') + name);

console.log(label2 + ": " + phoneNumber);


//Name:               zhangsan

//Phone Number: (555)-555-1234

现在两行都已填充。


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log((label1 + ': ').padEnd(20, ' ') + name);

console.log((label2 + ': ').padEnd(20, ' ') + phoneNumber);


//Name:               zhangsan

//Phone Number:       (555)-555-1234

数字(价格、日期、计时器等)呢?

padding函数是专门针对字符串而不是数字的,所以,我们需要先将数字转换为字符串。


价格

让我们看一下显示价格的初始代码。


const rmb = 10;

const cents = 1;

console.log("¥" + rmb + "." + cents); //¥10.1

要填充分,我们需要先将其转换为字符串,然后调用 padStart() 函数,指定长度为1且填充字符为'0';


const rmb = 10;

const cents = 1;

console.log("¥" + rmb + "." + cents.toString().padStart(2,0)); //¥10.01

日期

这是显示日期的初始代码。


const month = 2;

const year = 2020;


console.log(year + "-" + month); //2020-2

现在,让我们填充月份以确保它是两位数。


const month = 2;

const year = 2020;


console.log(year + "-" + month.toString().padStart(2,"0")); // 2020-02

计时器

最后是我们的计时器,我们要格式化两个不同的数字,即秒和毫秒。尽管有相同的原则。这是初始代码。


const seconds = 1;

const ms = 1;


console.log(seconds + ":" + ms); //1:1

现在要填充,我将在单独的行上进行填充,以便于阅读。


const seconds = 1;

const formattedSeconds = seconds.toString().padStart(2,0);

const ms = 1;

const formattedMs = ms.toString().padStart(3,0);


console.log(formattedSeconds + ":" + formattedMs); // 01:001

最后

虽然编写自己的padding函数并不难,但既然已经内置在JavaScript中,为什么还要自己去做呢?有很多有趣的函数已经内置了。在你自己构建一些东西之前,可能值得先快速搜索一下。

2020年越来越火的车载交互该怎么设计?来看前辈的经验总结!

雪涛

这次我们不聊视觉,也不畅想未来,只说说当下 HMI 产品设计与交互体验。

本文内容会涉及一些专业的汽车知识名词,因为篇幅有限,如有些知识名词不太明白可以百度一下。

别看错了,不是HDMI!

说到 HMI 大多数设计师应该是既熟悉又陌生,HMI 是 Human Machine Interface 的缩写,「人机接口」,也叫人机界面,人机界面(又称用户界面或使用者界面)是系统和用户之间进行交互和信息交换的媒介, 它实现信息的内部形式与人类可以接受形式之间的转换,凡参与人机信息交流的领域都存在着人机界面。

听起来是不是觉得这不就是 UI 吗?有什么区别吗?似乎差不多,几乎是没有区别的,只不过是在某些场合和设备上管他叫 UI,比如移动端设备,而在另外某些场所和设备上管他就叫 HMI,比如汽车车机和数控机床。所以这个概念也不用去特别较真,HMI 就权当作是汽车上的 UI 界面吧。毕竟汽车是高科技与工业结合的完美产物,「HMI」念出这个词时候就感觉是蛮专业的!很般配!

HMI前世与今生?

刚才说 HMI 最早更应用于工业上,比如常见的各种机床、制造装备。

或者说让时间再向前推进一点!

而这里通常意义的 HMI 则更加聚焦点,基本特指汽车车机或者车载多媒体设备。

说到这里还是要从车载仪表盘说起,从德国人卡尔·本茨发明世界第一辆汽车,距今已经 100 多年的时间了,在那些还没有 HMI 这个名词的年代,那么他是以什么形态出现的?那就不得不提「仪表盘」了。

当然写这篇文章并不是去评测谁家 HMI 更优秀,而是希望通过一些假设、实验和推断,和大家一起来探讨一下如何更有效地设计 HMI。

屏幕越大越好?车内到底需要几块屏幕?

我们先从屏幕开始。

说到屏幕,设计师都是比较敏感的,因为我们最终的设计交互创意都是需要都是在屏幕上显示展示出来的,HMI 当然也不例外。现在在车载屏幕上你能看到最大尺寸多大?

拿特斯拉为例,Model S 和 Model X 车型都是 17英寸,Model 3 为 15 英尺。

当然他肯定不是最大的,熟悉汽车朋友你应该知道我想说谁了,没错就是他!拥有 48 寸可多段升降屏幕的 BYTON 新能源概念车 M-Byte!48 寸的确很夸张,难道屏幕越来越大就是未来 HMI 的方向吗?

当然这个问题肯定是否定的,为什么?那就要从车载屏幕的作用来说起。

首先我是作为一个曾经就职于汽车公司的设计师,并且是一名地道的汽车发烧友,凭借对汽车还算熟悉和热爱做出一些产品交互分析,以下如有不妥之处还望海涵。

汽车内屏幕的作用

按照功能场景总体可分为三类:主行驶状态信息、附设备状态信息、多媒体 & 外设

不可缺少还需要与使用者、场景结合,我们先来做一个大概的用户画像。

对应这些需求,汽车需要有仪表台(屏)控制和显示的区域有五个。

五个区域分别是:

  • 主驾驶仪表屏
  • 中控台控制(屏)
  • 后排娱乐屏
  • 副驾驶信息屏
  • 扶手控制台(屏)

其中前三个是主流配置,后两个比较少见。

关于汽车设备这块我们不做深入展开了,毕竟这篇文章主要讨论的还是设计,直接看结果!

题外音:屏幕安全性的考量

汽车是比较特殊的设备,基于安全性考虑,汽车内屏幕尺寸不宜太大与太多。

屏幕总体为玻璃材质,但与车窗挡风玻璃的材质不同,当汽车遭遇碰撞的时候,车内屏幕极易破损并形成尖锐物,极大可能会乘坐人员造成二次伤害,所以车内屏幕不易太多,更不易太大。虽然车载屏幕变大变多已不可逆转,而且随着屏幕技术的提升,柔性 OLED 的应用也将会在一定范围解决安全问题。但也需要汽车相关设计者多在安全方面进行考虑,任何产品体验应该建立在安全基础之上的,特别是交通工具。

物理实体按钮过时了?

为什么大屏幕操控成为了当前的 HMI 主流了呢?那不得不去提一下另外一个我们熟悉的设备——手机!

同样一个有限的区域,如果用物理按键那么这个区域只能是固定的功能,而屏幕就可以无限扩展。特别是在汽车中控屏上集成内容会很多,体现就更加突出。

但是在汽车上的全部使用屏幕真的是最佳选择吗?显然这是有待商榷的。

不可否认屏幕的确有很强的扩展性,但是缺点也是明显的:1.触控反馈缺乏 2.交互效率不高

对于这样的判断,我们可以通过两个实验来进行验证。

将类似于 Surface Dial 这种智能按钮交互装置引入汽车的屏幕控制中,每个按钮可以根据情景进行自定义,并且吸附到汽车屏幕的任何位置进行交互操作,相信这一定是一种全新的使用体验。当然这一定是需要解决比如吸附力、安全性等一系列问题。

屏幕触控反馈

虽然目前的屏幕还无法做到完美触控反馈,但已经出现了一些新的硬件技术来试图解决这些问题,比如 Tanvas Touch,其定义为 「手指与触摸界面之间的电子压力控制」。简单来说他们的产品就 「皮肤的磁铁」 一样,能够更加精准地感应手指的动作,最后结果就是比 Apple 的 3D Touch 更加具有压感的触摸操作表现。

原理是利用手指尖触摸显示屏时产生的静电引力来模拟触感,通过电磁脉冲把更的反馈发送到用户的指尖。

Tanvas 也正在与汽车制造商们合作把这项技术嵌入到汽车或屏幕上,让人们更容易感触受到不同物体的表面。

也许在未来我们真的会遇到他。

文章来源:优设    作者:残酷de乐章

数据可视化指南:那些高手才懂的坐标轴设计细节

雪涛

坐标系是能够使每个数组在维度空间内找到映射关系的定位系统,更偏向数学/物理概念。在数据可视化中,最常用的坐标系分为笛卡尔坐标系和极坐标系,本文介绍的坐标轴设计主要也是围绕直角坐标系展开。

什么是坐标轴

在说坐标轴之前先来介绍下什么是坐标系。坐标系是能够使每个数组在维度空间内找到映射关系的定位系统,更偏向数学/物理概念。

维基百科对坐标系的定义是:对于一个 n 维系统,能够使每一个点和一组 n 个标量构成一一对应的系统,它可以用一个有序多元组表示一个点的位置。

数据可视化中,最常用的坐标系有两种:笛卡尔坐标系和极坐标系,均为二维坐标系。

  • 笛卡尔坐标系即直角坐标系,是由相互垂直的两条轴线构成。
  • 极坐标系由极点、极轴组成,坐标系内任何一个点都可以用极径和夹角(逆时针)表示。用到直角坐标系的常见图表有柱状图、折线图、面积图、条形图等。

下文介绍的坐标轴设计主要也是围绕直角坐标系展开,用到极坐标系的图表有饼图、圆环图、雷达图等。

坐标轴是坐标系的构成部分,是定义域轴和值域轴的统称。系的范围更大,而轴包含在系的概念里。由于可视化图表绘制的数据大部分都有一定的现实意义,因此我们可以根据坐标轴对应的变量是连续数据还是离散数据,将坐标轴分成连续轴、时间轴、分类轴三大类。轴的类型不同在设计处理上也有差异。

坐标轴的构成要素

介绍坐标轴设计前,我们先将坐标轴拆分成「原子」要素,具体分为轴线、轴刻度、轴标签、轴标题/单位、网格线。

坐标轴易被忽视的设计细节

根据坐标轴的构成,分类讨论下每个构成要素容易被忽视的设计细节。

轴线一般只考虑是否显示,例如柱状图、折线图等,在有背景网格线的情况下,会隐藏 y 轴线,条形图则是隐藏 x 轴线,以达到信息降噪,突出视觉重点的目的。

轴刻度通常不显示,只有在肉眼无法定位到某个标签对应的数据点时,会显示刻度线,辅助用户定位,比如折线图,或抽样显示的柱状图。

网格线用于定位数据点的值域范围,跟随值域轴的位置单向显示,柱状图采用水平网格,条形图采用垂直网格。样式为虚实线的最多,斑马线由于感知过强,一般不用。

轴标题/单位主要用于说明定义域轴、值域轴的数据含义。当可视化图表标题、图例、轴标签已经能充分表达数据含义,无需单独显示标题/单位,「如无必要,勿增实体」。

轴标签的设计就比较复杂,涉及到的细节点很多,而且对于定义域轴和值域轴上的标签和单位设计要考虑的细节点还有差异。下文将定义域轴和值域轴看成 x 轴和 y 轴,便于说明。

1. x轴标签设计

x 轴标签的设计重点在显示规则上,不同的坐标轴类型有不同的处理方式。

连续轴/时间轴的标签显示

连续轴/时间轴,是由一组前后包含同等差值的等差数列组成,缺少几个数值也能明显看出中间的对应关系。当多个标签在容器内全显示发生重叠,我们可以利用抽样显示的手段来避免这种情况。这里不推荐使用旋转,一方面从美观度上,旋转可能会破坏界面整体协调,另一方面,连续/时间轴非必须显示所有轴标签,抽样标签已经能满足用户对当前数组定义域的理解。

介绍一种常见的抽样方式:等分抽样

当多个标签在 x 轴无法完全显示,优先保留首尾标签,其余标签按同等步长间隔显示。间隔等分的前提是间隔数是合数,能被 1 和其本身以外的数整除。如果间隔数为质数,就需要「-1」转成合数。

举个例子:11 个标签,间隔数是 10,能被 2 和 5 整除,即分成 2 等分和 5 等分。12 个标签,间隔数是 11,无法等分,需要在间隔数基础上再「-1」,转成合数 10 后再等分,此时最后一个标签显示在倒数第二个数据点上。

有人会问了,能被那么多数等分,到底该选哪个呢?这就要根据标签长度来定,选择能放下最多标签的等分值。由于连续轴/时间轴,一般是数值、日期、时间等,字符长度有限,即使抽样后也能保证显示出一定数量的标签。

等分抽样不太适用于表达某个时间周期内的走势折线图,因为最后一个标签不一定对应最后一个数据点。对于这类折线图,能清楚表明起始时间和末尾时间,相比显示更多时间标签重要性来的更高。设计上可以只显示首尾标签,或首尾 + 中间值。

分类轴的标签显示

分类轴是由几组离散数据组成,相互之间独立存在,无紧密逻辑关联。若采用抽样规则,隐藏一些标签,用户对图表认知就会有困难,违背了数据可视化清晰、有效的设计原则。分类轴最佳处理方式是标签旋转 45 度,若 45 度仍显示不下,继续旋转 90 度。如果 90 度还是放不下就要考虑结合图表交互或反转图表。

标签旋转方向也有讲究,因为人的视觉习惯是从左到右,从上到下,标签顺时针旋转 45 度更符合用户的浏览动线。

分类轴标签字段有长有短,长文本标签直接旋转不仅影响美观,而且也不利于用户阅读。如果数据量比较少只有 2~4 个,长文本标签更适合水平展示,显示不下省略即可;如果数据量比较多,就限定字符数后旋转。

2. y轴标签设计

y 轴标签的设计重点在标签数量、取值范围和数据格式上。标签显示区域一般根据最长标签宽度自适应缩放。如果数组是固定的,就写成固定宽度,节省图表计算量,提高渲染速度。

y轴标签数量

标签数量不建议过多,太多的标签必定导致横向网格线变多,造成元素冗余,干扰图形信息表达。根据 7±2 设计原则,y 轴标签数量最多不超过这个范围。

y轴标签取值范围

y 轴标签的取值范围决定了图形在整个绘图区域的显示高度。

折线图 y 轴标签取值一般保证图形约占绘图区域的 2/3,以更有效的传达数据波动幅度,避免掩盖和夸大变化趋势。2/3 即斐波那契数列第二位起,相邻两数之比,也是黄金分割最简单的计算方法。

柱状图的 y 轴标签取值应从 0 基线开始,以恰当反映数值。如果展示被截断的柱状图,可能会误导观众做出错误的判断。

y轴标签数据格式

y 轴标签的数据格式在 ant.vision 写的比较详细,重复内容不在此说明,重点讲下一些特殊的设计细节。标签保留的小数位数保持统一,不要因为某些轴标签是整数值,就略去小数点。

正负向的 y 轴标签,由于负值带「-」符号,整个 y 轴看起来会有视觉偏差,特别是双轴图的右 y 轴更明显。这里建议正负向 y 轴给正值标签带上「+」,以达到视觉平衡的效果。

总结

写了那么多关于坐标轴的设计,你是不是恍然大悟,原来小小的坐标轴还有如此之多的细节。往常我们做图表设计,可能只是用网上自动生成的图表简单调整下,或者按照通用样式来设计。然而,通用样式虽然能表达数据意义,但也缺少了对图表细节的把控,失了精致优雅的感觉。

作为数据可视化设计的一小部分,就是这些设计细节,决定了图表最终的传达效果。

文章来源:优设    作者:米粒的DesignNote

手机appUI界面设计赏析(一)

前端达人

与传统PC桌面不同,手机屏幕的尺寸更加小巧操作,方式也已触控为主,APP界面设计不但要保证APP功能的完整性和合理性,又要保证APP的功能性和实用性,在保证其拥有流畅的操作感受的同时,满足人们的审美需求。

接下来为大家介绍几款手机appui界面设计

点击查看原图

--手机appUI设计--

点击查看原图

--手机appUI设计--

点击查看原图

--手机appUI设计--

点击查看原图

--手机appUI设计--

点击查看原图

--手机appUI设计--

点击查看原图


--手机appUI设计--



微信图片_20200529093951.jpg

--手机appUI设计--


微信图片_20200529093948.png

--手机appUI设计--


微信图片_20200529093946.png


--手机appUI设计--


点击查看原图

--专业又贴心医疗App页面设计--


微信图片_20200529093941.jpg

--专业又贴心医疗App页面设计--微信图片_20200529093938.jpg

--专业又贴心医疗App页面设计--微信图片_20200529093936.jpg

--专业又贴心医疗App页面设计--微信图片_20200529093933.jpg

--专业又贴心医疗App页面设计--微信图片_20200529093930.jpg

--手机appUI设计--


微信图片_20200529093928.jpg

--手机appUI设计--


微信图片_20200529093925.jpg

--手机appUI设计--


微信图片_20200529093921.jpg

--手机appUI设计--


点击查看原图

--手机appUI设计--


点击查看原图

--手机appUI设计--


点击查看原图

--手机appUI设计--


点击查看原图

--手机appUI设计--


点击查看原图

--手机appUI设计--


点击查看原图


--手机appUI设计--


点击查看原图

--手机appUI设计--


点击查看原图


--手机appUI设计--


--手机appUI设计--


点击查看原图



--手机appUI设计--

(以上图片均来源于网络)



  蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服



   更多精彩文章:

       手机appUI界面设计赏析(二)




JavaScript版数据结构与算法——基础篇(一)

前端达人

数组

数组——最简单的内存数据结构

数组存储一系列同一种数据类型的值。( Javascript 中不存在这种限制)

对数据的随机访问,数组是更好的选择,否则几乎可以完全用 「链表」 来代替

在很多编程语言中,数组的长度是固定的,当数组被填满时,再要加入新元素就很困难。Javascript 中数组不存在这个问题。

但是 Javascript 中的数组被实现成了对象,与其他语言相比,效率低下。

数组的一些核心方法

方法 描述
push 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。(改变原数组)
pop 方法从数组中删除最后一个元素,并返回该元素的值。(改变原数组)
shift 方法从数组中删除第一个元素,并返回该元素的值,如果数组为空则返回 undefined 。(改变原数组)
unshift 将一个或多个元素添加到数组的开头,并返回该数组的新长度(改变原数组)
concat 连接两个或多个数组,并返回结果(返回一个新数组,不影响原有的数组。)
every 对数组中的每个元素运行给定函数,如果该函数对每个元素都返回 true,则返回 true。若为一个空数组,,始终返回 true。 (不会改变原数组,[].every(callback)始终返回 true)
some 对数组中的每个元素运行给定函数,如果任一元素返回 true,则返回 true。若为一个空数组,,始终返回 false。(不会改变原数组,)
forEach 对数组中的每个元素运行给定函数。这个方法没有返回值,没有办法中止或者跳出 forEach() 循环,除了抛出一个异常(foreach不直接改变原数组,但原数组可能会被 callback 函数该改变。)
map 对数组中的每个元素运行给定函数,返回每次函数调用的结果组成的数组(map不直接改变原数组,但原数组可能会被 callback 函数该改变。)
sort 按照Unicode位点对数组排序,支持传入指定排序方法的函数作为参数(改变原数组)
reverse 方法将数组中元素的位置颠倒,并返回该数组(改变原数组)
join 将所有的数组元素连接成一个字符串
indexOf 返回第一个与给定参数相等的数组元素的索引,没有找到则返回 -1
lastIndexOf 返回在数组中搜索到的与给定参数相等的元素的索引里最大的值,没有找到则返回 -1
slice 传入索引值,将数组里对应索引范围内的元素(浅复制原数组中的元素)作为新数组返回(原始数组不会被改变)
splice 删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容(改变原数组)
toString 将数组作为字符串返回
valueOf 和 toString 类似,将数组作为字符串返回

是一种遵循后进先出(LIFO)原则的有序集合,新添加或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。

通俗来讲,就是你向一个桶里放书本或者盘子,你要想取出最下面的书或者盘子,你必须要先把上面的都先取出来。

栈也被用在编程语言的编译器和内存中保存变量、方法调用等,也被用于浏览器历史记录 (浏览器的返回按钮)。

代码实现

// 封装栈
    function Stack() {
        // 栈的属性
        this.items = []

        // 栈的操作
        // 1.将元素压入栈
        Stack.prototype.push = function (element) {
            this.items.push(element)
        }
        // 2.从栈中取出元素
        Stack.prototype.pop = function () {
            return this.items.pop()
        }
        // 3.查看栈顶元素
        Stack.prototype.peek = function () {
            return this.items[this.items.length - 1]
        }
        // 4.判断是否为空
        Stack.prototype.isEmpty = function () {
            return this.items.length === 0
        }
        // 5.获取栈中元素的个数
        Stack.prototype.size = function () {
            return this.items.length
        }
        // 6.toString()方法
        Stack.prototype.toString = function () {
            let str = ''
            for (let i = 0; i< this.items.length; i++) {
                str += this.items[i] + ' '
            }
            return str
        }

    }

    // 栈的使用
    let s = new Stack()

队列

队列是遵循先进先出(FIFO,也称为先来先服务)原则的一组有序的项。队列在尾部添加新
元素,并从顶部移除元素。添加的元素必须排在队列的末尾。

生活中常见的就是排队

代码实现

function Queue() {
        this.items = []
        // 1.将元素加入队列
        Queue.prototype.enqueue = function (element) {
            this.items.push(element)
        }
        // 2.从队列前端删除元素
        Queue.prototype.dequeue = function () {
            return this.items.shift()
        }
        // 3.查看队列前端元素
        Queue.prototype.front = function () {
            return this.items[0]
        }
        // 4.判断是否为空
        Queue.prototype.isEmpty = function () {
            return this.items.length === 0
        }
        // 5.获取队列中元素的个数
        Queue.prototype.size = function () {
            return this.items.length
        }
        // 6.toString()方法
        Queue.prototype.toString = function () {
            let str = ''
            for (let i = 0; i< this.items.length; i++) {
                str += this.items[i] + ' '
            }
            return str
        }
    }
    
    // 队列使用
    let Q = new Queue()

优先级队列:

代码实现


function PriorityQueue() {
        function QueueElement(element, priority) {
            this.element = element
            this.priority = priority
        }
        this.items = []

        PriorityQueue.prototype.enqueue = function (element, priority) {
            let queueElement = new QueueElement(element,priority)

            // 判断队列是否为空
            if (this.isEmpty()) {
                this.items.push(queueElement)
            } else {
                let added = false // 如果在队列已有的元素中找到满足条件的,则设为true,否则为false,直接插入队列尾部
                for (let i = 0; i< this.items.length; i++) {
                    // 假设priority值越小,优先级越高,排序越靠前
                    if (queueElement.priority < this.items[i].priority) {
                        this.items.splice(i, 0, queueElement)
                        added = true
                        break
                    }
                }
                if (!added) {
                    this.items.push(queueElement)
                }
            }

        }
        
    }
    

链表

链表——存储有序的元素集合,但在内存中不是连续放置的。


链表(单向链表)中的元素由存放元素本身「data」 的节点和一个指向下一个「next」 元素的指针组成。牢记这个特点

相比数组,链表添加或者移除元素不需要移动其他元素,但是需要使用指针。访问元素每次都需要从表头开始查找。

代码实现:
单向链表


function LinkedList() {
        function Node(data) {
            this.data = data
            this.next = null

        }
        this.head = null // 表头
        this.length = 0
        // 插入链表
        LinkedList.prototype.append = function (data) {
            // 判断是否是添加的第一个节点
            let newNode = new Node(data)
            if (this.length == 0) {
                this.head = newNode
            } else {
                let current = this.head
                while (current.next) { 
                // 如果next存在,
                // 则当前节点不是链表最后一个
                // 所以继续向后查找
                    current = current.next
                }
                // 如果next不存在
                 // 则当前节点是链表最后一个
                // 所以让next指向新节点即可
                current.next = newNode
            }
            this.length++
        }
        // toString方法
        LinkedList.prototype.toString = function () {
            let current = this.head
            let listString = ''
            while (current) {
                listString += current.data + ' '
                current = current.next
            }
            return listString
        }
         // insert 方法
        LinkedList.prototype.insert = function (position, data) {
            if (position < 0 || position > this.length) return false
            let newNode = new Node(data)
            if (position == 0) {
                newNode.next = this.head
                this.head = newNode
            } else {
                let index = 0
                let current = this.head
                let prev = null
                while (index++ < position) {
                    prev = current
                    current = current.next
                }
                newNode.next = current
                prev.next = newNode
            }
            this.length++
            return true
        }
        // get方法
        LinkedList.prototype.get = function (position) {
            if (position < 0 || position >= this.length) return null
            let index = 0
            let current = this.head
            while (index++ < position){
                current = current.next
            }
            return current.data
        }
        LinkedList.prototype.indexOf = function (data) {
            let index = 0
            let current = this.head
            while (current) {
                if (current.data == data) {
                    return index
                } else {
                    current = current.next
                    index++
                }
            }

            return  -1
        }
        LinkedList.prototype.update = function (position, data) {
            if (position < 0 || position >= this.length) return false
            let index = 0
            let current = this.head
            while (index++ < position) {
                current = current.next
            }
            current.data = data
            return  true
        }
        LinkedList.prototype.removeAt = function (position) {
            if (position < 0 || position >= this.length) return null
            if (position == 0) {
                this.head = this.head.next
            } else {
                let index = 0
                let current = this.head
                let prev = null
                while (index++ < position) {
                    prev = current
                    current = current.next
                }
                prev.next = current.next
            }
            this.length--
            return  true


        }
        LinkedList.prototype.remove = function (data) {
            let postions = this.indexOf(data)

            return this.removeAt(postions)
        }
        
    }

    let list = new LinkedList()
双向链表:包含表头表尾 和 存储数据的 节点,其中节点包含三部分:一个链向下一个元素的next, 另一个链向前一个元素的prev 和存储数据的 data牢记这个特点

function doublyLinkedList() {
        this.head = null // 表头:始终指向第一个节点,默认为 null
        this.tail = null // 表尾:始终指向最后一个节点,默认为 null
        this.length = 0 // 链表长度

        function Node(data) {
            this.data = data
            this.prev = null
            this.next = null
        }

        doublyLinkedList.prototype.append = function (data) {
            let newNode = new Node(data)

            if (this.length === 0) {
            // 当插入的节点为链表的第一个节点时
            // 表头和表尾都指向这个节点
                this.head = newNode
                this.tail = newNode
            } else {
            // 当链表中已经有节点存在时
            // 注意tail指向的始终是最后一个节点
            // 注意head指向的始终是第一个节点
            // 因为是双向链表,可以从头部插入新节点,也可以从尾部插入
            // 这里以从尾部插入为例,将新节点插入到链表最后
            // 首先将新节点的 prev 指向上一个节点,即之前tail指向的位置
                newNode.prev = this.tail
            // 然后前一个节点的next(及之前tail指向的节点)指向新的节点
            // 此时新的节点变成了链表的最后一个节点
                this.tail.next = newNode
            // 因为 tail 始终指向的是最后一个节点,所以最后修改tail的指向
                this.tail = newNode
            }
            this.length++
        }
        doublyLinkedList.prototype.toString = function () {
            return this.backwardString()
        }
        doublyLinkedList.prototype.forwardString = function () {
            let current = this.tail
            let str = ''

            while (current) {
                str += current.data + ''
                current = current.prev
            }

            return str
        }
        doublyLinkedList.prototype.backwardString = function () {
            let current = this.head
            let str = ''

            while (current) {
                str += current.data + ''
                current = current.next
            }

            return str
        }

        doublyLinkedList.prototype.insert = function (position, data) {
            if (position < 0 || position > this.length) return false
            let newNode = new Node(data)
            if (this.length === 0) {
                this.head = newNode
                this.tail = newNode
            } else {
                if (position == 0) {
                    this.head.prev = newNode
                    newNode.next = this.head
                    this.head = newNode
                } else if (position == this.length) {
                    newNode.prev = this.tail
                    this.tail.next = newNode
                    this.tail = newNode
                } else {
                    let current = this.head
                    let index = 0
                    while( index++ < position){
                        current = current.next
                    }
                    newNode.next = current
                    newNode.prev = current.prev
                    current.prev.next = newNode
                    current.prev = newNode

                }

            }

            this.length++
            return true
        }
        doublyLinkedList.prototype.get = function (position) {
            if (position < 0 || position >= this.length) return null
            let current = this.head
            let index = 0
            while (index++) {
                current = current.next
            }

            return current.data
        }
        doublyLinkedList.prototype.indexOf = function (data) {
            let current = this.head
            let index = 0
            while (current) {
                if (current.data === data) {
                    return index
                }
                current = current.next
                index++
            }
            return  -1
        }
        doublyLinkedList.prototype.update = function (position, newData) {
            if (position < 0 || position >= this.length) return false
            let current = this.head
            let index = 0
            while(index++ < position){
                current = current.next
            }
            current.data = newData
            return true
        }
        doublyLinkedList.prototype.removeAt = function (position) {
            if (position < 0 || position >= this.length) return null
            let current = this.head
            if (this.length === 1) {
                this.head = null
                this.tail = null
            } else {
                if (position === 0) { // 删除第一个节点
                    this.head.next.prev = null
                    this.head = this.head.next
                } else if (position === this.length - 1) { // 删除最后一个节点
                    this.tail.prev.next = null
                    this.tail = this.tail.prev
                } else {
                    let index = 0
                    while (index++ < position) {
                        current = current.next
                    }
                    current.prev.next = current.next
                    current.next.prev = current.prev
                }
            }
            this.length--
            return current.data
        }
        doublyLinkedList.prototype.remove = function (data) {
            let index = this.indexOf(data)
            return this.removeAt(index)
        }
    }


感谢你的阅读~
————————————————
版权声明:本文为CSDN博主「重庆崽儿Brand」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/brand2014/java/article/details/106134844



大数据可视化设计赏析(三)

前端达人

     如今大数据产业正在超出我们的想象悄然发展,而随着大数据时代的到来,越来越多的公司开始意识到数据资源的管理和运用。今天就给大家介绍一些可视化大屏的UI设计。


点击查看原图

    --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

c24b7efe812270eb555c5ab24b8a5fa2626973621ab956-4LUO4k_fw658.gif

 --大屏UI设计--

eebdcf2ab80ccf28a832b463b5efb8d390baa8401fbcda-58EU2O_fw658.jpg


eee7b0bd72a92d26ef0ea8b65921a2fcacf49ae934f18-ScQnAI_fw658.png

f0ab44b8e812af72209891521cbff1fe6ff656b863d09-JxGZiR_fw658.jpg



f5c7bedb9779f20ca239e235a98ef8eae839a5f980e8a-gkXvyM_fw658.png

 --大屏UI设计--


点击查看原图

 --大屏UI设计--

TB2XULnmC0mpuFjSZPiXXbssVXa-680650857的副本.jpg

 --大屏UI设计--点击查看原图

 --大屏UI设计--点击查看原图

 --大屏UI设计--点击查看原图

 --大屏UI设计--点击查看原图

 --大屏UI设计--点击查看原图

 --大屏UI设计--点击查看原图

 --大屏UI设计--点击查看原图

 --大屏UI设计--WechatIMG166.jpeg

 --大屏UI设计--点击查看原图

 --大屏UI设计--点击查看原图

 --大屏UI设计--点击查看原图

 --大屏UI设计--点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

WechatIMG174.jpeg

 --大屏UI设计--

WechatIMG175.jpeg

 --大屏UI设计--

WechatIMG164.jpeg

 --大屏UI设计--

WechatIMG176.jpeg

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

点击查看原图

 --大屏UI设计--

(以上图片均来自于网络)


其实可视化大屏的UI设计并不只是一个简单的设计,其核心就是要以展示数据为核心,不管在多么炫目的情况下都不会影响数据的展示。


  蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服





    更多精彩文章:

       

 大数据可视化设计赏析(一)

  大数据可视化设计赏析(二)

  大数据可视化设计赏析(三)

  大数据可视化设计赏析(四)

  大数据可视化设计赏析(五)

  大数据可视化设计赏析(六)

  大数据可视化设计赏析(七)




日历

链接

blogger

蓝蓝 http://www.lanlanwork.com

存档