在CSS中解决内容过长的问题

当我们写css的时候,有时候会忘记设计里面存在的临界情况。举个例子来说吧,当内容的长度超过了我们的期望值,我们也无法解释其中的可能性,页面的设计很可能会因此而崩掉。我们不能保证css总是会按照我们期望的那样工作,但至少我们可以用不同类型的内容来测试,以减少这种情况的发生。 在这篇文章里,我们通过审查真实网站的各种的UI问题来解释这些网站可能会崩溃的原因。准备好了么?来吧! 一个右侧/左侧有小图标的按钮 这是一个手风琴效果的开关按钮。按钮右侧有一个小图标用来强调它是可点击的。然而当按钮的区域不够长的时候,按钮上的文字会盖住图标。当我们没有考虑到较长内容的时候这种情况就可能发生。 一个解决办法是在右侧增加足够的padding值来适应图标的大小 .button { padding-right: 50px;}注意我们是如何增加padding值来给图标创造出一块安全的显示区域的,现在我们可以确定按钮的布局不会再被破坏了。 输入占位符当在我们的论坛使用浮动标注模式的时候,特别是按钮在右侧的这种情况,我们需要充分的测试来避免因为占位符过长而导致的问题。 一个解决办法是给按钮添加 position: relative,这样会让按钮覆盖在占位符上层。 长名字 在这个设计中,图片向左浮动,右侧有作者名字的信息。当右侧的信息长度过长的时候会发生什么呢?毫无疑问布局会崩掉。 这里的问题是我们只向左浮动了图片,使得作者的名字移动到贴着它,但是这只会在作者名字长度较短的时候才会表现良好。 为了使页面布局的适应性更强,我们需要给这两个元素都增加 width。更推荐的方式是使用flexbox,更适合这样的小型组件。 文章内有长链接/单词 有时文章内会包含该一些很长的超链接或者单词,当在视窗很宽的时候可能不会造成问题。但是对于一些尺寸较小的设备,诸如手机或平板电脑,这可能会产生烦人的横向滚动条。 对于这个问题我们有两个解决方案: 1)使用CSS word-break.article-body p { word-break: break-all;}word-break属性在不同的浏览器内表现不太一样,因此在使用的时候需要充分测试。 2) 给外层元素添加overflow和 text-overflow.article-body p { overflow: hidden; text-overflow: ellipsis;}这个方案对于过长的链接比较友好,对于长单词,我推荐使用 word-break。 过长的文章标签当我们放置一个文章标签在卡片上,我们最好只通过设定它的padding来确定它的大小。当标签的内容过长的时候,写死高度和宽度可能会造成布局崩掉。 也可以给标签设定一个最小的宽度,当对padding包裹的标签内容元素使用min-width属性时,宽度是动态变化的,问题就解决了。 带有固定链接的段落头这个例子是在段落标题的右侧有一个‘view more’链接。有几种不同的方式来编写CSS,其中一种是对链接使用绝对定位。 当标题过长的时候可能会造成一些问题,一个更好的解决办法是使用flexbox布局,这样的话当没有足够空间的时候会自动将链接挤到下一行去。 .header-2 { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;}上面这个技巧被称呼为'对齐移动包裹'。

May 8, 2019 · 1 min · jiezi

Hello-CSS第三章浏览器的视图与坐标

作者:陈大鱼头github: KRISACHAN在上一篇【Hello CSS】的第二章第二章-CSS的逻辑属性与盒子模型中提了个问题: 为什么Flex box跟Grid box的是以start、end为排列规则,而不是常规的top 、right 、bottom 跟left? 现在各位看官有答案了吗? 因为上一篇中有提到过CSS 逻辑属性的变革,从物理概念跳跃到了逻辑概念,也就是从top 、 right 、 bottom、 left更新为block、inline、start、 end。由于Flex box跟Grid box是CSS3的布局模式,所以自然而然会使用更加适应于新时代的语言属性。 上一篇主要介绍了CSS的逻辑属性跟盒子模型的基本现状。本篇则会介绍浏览器的视图与坐标。 关于设备屏幕,我们应该知道的知识像素(Pixel)像素(pixel)是影像显示的基本单位,一个像素通常被视为影像的最小的完整取样。用来表示一幅影像的像素越多,结果更接近原始的影像。 我们看看下图,图中最小的点就是设备的像素。 分辨率(Image resolution)分辨率(Image resolution)日常用语中之分辨率多用于影像的清晰度。分辨率越高代表影像质量越好,越能表现出更多的细节。 显示分辨率列表:https://zh.wikipedia.org/wiki/显示分辨率列表 下图是不同分辨率下的图像显示 每英寸像素(PPI)每英寸像素(英语:Pixels Per Inch,缩写:PPI),又被称为像素密度,是一个表示打印图像或显示器单位面积上像素数量的指数。一般用于计量电子设备屏幕的精细程度。通常情况下,每英寸像素值越高,屏幕能显示的图像也越精细。如上面分辨率的图显示。 视网膜显示屏(Retina Display)视网膜显示屏(Retina Display)是一种由苹果公司设计和委托制造的显示屏。有研究表明,人类肉眼能够分辨的最高PPI是300PPI,所以超过PPI超过300的往往被称为Retina显示屏,这个概念是不对的,Retina显示屏指的是在人体正常使用距离下,无法用肉眼看到屏幕像素的显示屏。 下图为显示了“地图”二字的Retina显示屏 每英寸点数(DPI)DPI(英语:Dots Per Inch,每英寸点数)是一个量度单位,用于点阵数位影像,意思是指每一英寸长度中,取样或可显示或输出点的数目。如:打印机输出可达600DPI的分辨率,表示打印机可以在每一平方英寸的面积中可以输出600X600=360000个输出点。 下图为喷墨打印机以较低质量在约 0.25 平方英寸的范围内按 150 dpi 打印的效果的特写 设备独立像素(DIP, DP)设备独立像素(Device Independent Pixels,DIP,又称设备无关像素)是一种物理测量单位,基于计算机控制的坐标系统和抽象像素(虚拟像素),是定义UI布局时使用的虚拟像素单位。 设备像素比(DPR)设备像素比(DPR)是设备上物理像素和DIP的比例。公式如下: window.devicePixelRatio = 物理像素 / dipsCSS像素(CSS Pixels)CSS像素(CSS Pixels)是WEB编程中诞生的概念,用于定于浏览器中每个模型不同CSS的值大小。由于CSS像素(CSS Pixels)是个逻辑性的像素,而非物理性的像素,所以1个CSS像素在不同设备上大小可能会有不同。但即便是如此,对于CSS来说,还是希望在不同设备上大小尽可能地看起来相同。 那么这该如何实现呢?基于这个问题,W3C提出参考像素这个概念。定义如下: 参考像素是设备上一个像素的视角,像素密度为96dpi,离设备长一臂。标准的手臂长度为28英尺,所以视角大概为0.0213度。对于臂长的读数,1px应该为0.26mm(1/96 英尺)。如下图: 所以1px CSS像素大小该是多少?基于这个问题,W3C给出的答案如下: ...

May 7, 2019 · 2 min · jiezi

Hello-CSS第二章CSS的逻辑属性与盒子模型

作者:陈大鱼头github: KRISACHAN在上一篇【Hello CSS】的第一章CSS的语法与工作流中介绍了CSS的语法规则以及基本的渲染流程。本篇则会分享CSS的逻辑属性以及盒子模型。 首先开篇之前先提个问题: 为什么Flex box跟Grid box的是以start、end为排列规则,而不是常规的top 、right 、bottom 跟left? 先不要急着往下翻,大家先思考一下。 这个问题的答案,鱼头会在文章中给出,欢迎大家带着这个问题往下翻阅,如果已经知道答案,也可以看看跟大家所知道的答案是否一致。 CSS的逻辑属性2017年5月18日,W3C的 CSS工作组(CSS Working Group) 发布了 CSS逻辑属性和值(CSS Logical Properties and Values Level 1) 的首份工作草案(First Public Working Draft)。不同的书写模式(writing mode)中,可以抽取出共性的抽象概念(如开始位置,或行),这些逻辑抽象概念需要在不同书写模式下映射到左或右、上或下等物理的概念上。一些CSS布局可能依赖这些共性的逻辑概念。该 CSS 模块给出了用于通过逻辑方式(而不是基于物理坐标、书写方向和维映射等)控制布局的逻辑属性和取值(logical properties and values)。这个模块来源于CSS21中关于逻辑属性和值的特性。上面复制粘贴了W3C 中国里的内容。 对于前端来说,我们一直习惯于使用top 、 right 、 bottom、 left来定义我们的HTML元素,这跟我们物理上的概念是一致的。但是对于CSS这个原本是为了服务于图文展示才诞生的语言来说,其实是不匹配的,为什么这么说? writing-modewriting-mode:定义了文本水平或垂直排布以及在块级元素中文本的行进方向。writing-mode一共有以下5个改变HTML文本书写规则的值(还有几个是用在SVG上的,本文不予讨论): writing-mode: horizontal-tb;writing-mode: horizontal-tb 定义了内容从左到右水平流动,从上到下垂直流动。下一条水平线位于上一条线下方。 writing-mode: vertical-rl;writing-mode: vertical-rl 定义了内容从上到下垂直流动,从右到左水平流动。下一条垂直线位于上一行的左侧。 writing-mode: vertical-lr;writing-mode: vertical-lr定义了内容从上到下垂直流动,从左到右水平流动。下一条垂直线位于上一行的右侧。 writing-mode: sideways-rl; (仅Firefox41+实现)writing-mode: sideways-rl定义了内容从上到下垂直流动,所有字形,甚至是垂直脚本中的字形,都设置在右侧。 writing-mode: sideways-lr;(仅Firefox41+实现)writing-mode: sideways-lr内容从上到下垂直流动,所有字形,甚至是垂直脚本中的字形,都设置在左侧。 上述效果请看DEMO 源码如下: .wm-htb { writing-mode: horizontal-tb;}.wm-vrl { writing-mode: vertical-rl;}.wm-vlr { writing-mode: vertical-lr;}.wm-srl { writing-mode: sideways-rl;}.wm-slr { writing-mode: sideways-lr;}.text-content { width: 200px; padding: 20px; border: 1px solid; display: inline-block; vertical-align: top; padding-right: 100px;}<div class="text-content wm-htb">writing-mode: horizontal-tb;</div><div class="text-content wm-vrl">writing-mode: vertical-rl;</div><div class="text-content wm-vlr">writing-mode: vertical-lr;</div><div class="text-content wm-srl">writing-mode: sideways-rl;</div><div class="text-content wm-slr">writing-mode: sideways-lr;</div>图示如下: ...

May 7, 2019 · 2 min · jiezi

CSS-搞怪的-textdecoration

今天在改一个项目的时候却遇到了一个莫名其妙的属性:text-decoration,这个属性,其实就只是用来把一段文字加上上横线、删除线或底线的属性罢了,通常会用在移除超链接的底线、或一些特殊强调的效果里头,但是这么容易的属性,为什么会莫名其妙呢?就让我们继续看下去~ 项目里遇到的问题在我的项目里头遇到的问题如下,一个div里头包了一个span,我要“除了这个span之外,其他所有的文字都有底线”,通常看到这个问题一定觉得很简单,只要照下面的CSS写法一定可以达成: div{ font-size:20px; text-decoration: underline;}div span{ text-decoration: none;}理论上应该前一段会有底线,后一段会没有底线,但是实际上却是一条底线通到底….. 不过我不信邪,怕是哪里CSS权重出了问题,直接加上万恶的important试试看,结果发现结果还是一模一样!天呀!是见到鬼了吗! div{ font-size:20px; text-decoration: underline;}div span{ text-decoration: none!important;} 由于实在是太诡异了,必须要查明原因,于是我换个角度思考,来改一下颜色试试看吧!一改才发现不得了,为什么底线会是红色的呀?! div{ font-size:20px; color:#f00; text-decoration: underline;}div span{ color:#00f; text-decoration: none!important;} text-decoration属性定义看到这边我已经大概知道原因了,最有可能的原因应该就是出在text-decoration这个属性的定义,经过一番追根究柢,总算看到W3C的说法,主要是因为text-decoration会把整个父元素加上底线,而整个父元素,当然就包含了子元素,因为同样颜色的缘故,就以为子元素也被加上底线了,(实际上子元素没有被加上底线),不过后来又看了这个专门分析兼容性问题的网站说明,其实text-decoration会根据不同的display属性,而决定父元素的底线是否延伸,举例来说吧!如果今天我把span的display改成inline-block,就会得到不同的结果: div{ font-size:20px; color:#f00; text-decoration: underline;}div span{ display:inline-block; color:#00f; text-decoration: none!important;} 小结不过由于各家浏览器的渲染不同,加上已经明白了个中原理,这里就不做太多的测试,只要记得下次如果又遇到这种问题,可能就是父元素和子元素互相干扰所造成,自己也要特别留心。最后,好像也可以利用这种方法,做出莫名其妙的效果…HTML <div> <span><span><span>真是很莫名其妙的效果<span></span></span></div>CSS div{ font-size:30px; color:#f00; text-decoration:overline;}div span{ color:#00f; text-decoration: line-through;}div span span{ color:#0f0; text-decoration: underline;}div span span span{ color:#000; text-decoration: none;}

May 7, 2019 · 1 min · jiezi

如何使用-css3-transform-属性来变换背景图

使用 css3 transform 属性可以轻易的旋转,倾斜,缩放任何元素。目前即使没有任何前缀也可以在绝大部分浏览器上很好的使用。 #myelement {-webkit-transform: rotate(30deg);transform: rotate(30deg);}这个听起来很赞。然而,这个属性旋转了整个元素,包括他的内容、边框、背景图。如果你只是想旋转它的背景图而不选旋转内容的话,应该怎么做呢?或者你只想旋转内容,而不旋转背景图,这个又该怎么做呢? 目前 W3C 没有关于如何旋转背景图的提案。我觉得这个是非常常见的使用场景,我深信最终也会出来相相关提案,但这对当前就想要实现这个效果的开发者没有什么意义。 幸运的是,我们找到一个解决方式。这个方式本质上,是将背景图应用到某个元素的 before 或者 after 这种伪类元素上而不是应用到元素本身。然后在伪类元素独立的使用 transform 属性。 仅仅变换背景这个元素可以使用任何样式,但一定要设置 position 属性,因为其伪类元素会基于它来定位。如果不想背景撑到元素外,那就要设置 overflow: hidden。 #myelement {position: relative;overflow: hidden;}现在我们可以创建一个绝对定位的伪类元素来实现变换背景。为了确保他会低于元素内容显示,需要设置 z-index: -1。 #myelement:before {content: "";position: absolute;width: 200%;height: 200%;top: -50%;left: -50%;z-index: -1;background: url(background.png) 0 0 repeat;-webkit-transform: rotate(30deg);transform: rotate(30deg);}需要注意的是,在变换的过程中,你需要去调整伪类元素的 width,height,position 属性。例子:假如伪类元素使用了一张可重复的图片做背景,那么旋转区域就必须大于父元素,这样才可以在旋转过程中覆盖整个父元素。 在变换的元素上实现固定背景所有主元素的变换操作都会影响到伪类元素. 假如伪类元素不想要变换操作时,我们就需要撤销这个变换, 例子:当一个父元素旋转了 30 度,那么伪类元素需要相反方向旋转 30 度,来使伪类元素回退到固定位置。 #myelement {position: relative;overflow: hidden;-webkit-transform: rotate(30deg);transform: rotate(30deg);}#myelement:before {content: "";position: absolute;width: 200%;height: 200%;top: -50%;left: -50%;z-index: -1;background: url(background.png) 0 0 repeat;-webkit-transform: rotate(-30deg);transform: rotate(-30deg);}再次强调,你需要对伪类元素的宽高及定位属性进行调整来确保它可以完全覆盖主元素。 ...

May 7, 2019 · 1 min · jiezi

块级元素的三种垂直水平居中的方法

直奔主题在这里提供三种块级元素垂直水平居中的方法 flex(子级宽高可固定也可不固定,随意)定位+margin(固定子级的宽高,margin-top,margin-left取自身一半的负值)定位+transform(不固定子级的宽高)flex html<div class="parent"> <div class="child"></div></div>css.parent{ width: 500px; height: 500px; margin: 0 auto; border: 1px solid gainsboro; display: flex; justify-content: center; align-items: center;}.child{ width: 200px; height: 200px; background: red;}定位+margin html<div class="parent"> <div class="child"></div></div>css.parent{ position: relative; width: 500px; height: 500px; margin: 0 auto; border: 1px solid gainsboro; }.child{ position: absolute; top: 50%; left: 50%; width: 200px; height: 200px; margin-left: -100px; margin-top: -100px; background: red; }定位+transform html<div class="parent"> <div class="child"> <span>我是子元素</span> </div></div>css.parent{ position: relative; width: 500px; height: 500px; margin: 0 auto; border: 1px solid gainsboro; } .child{ position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); background: red; }个人更喜欢flex ...

May 7, 2019 · 1 min · jiezi

容易忽略的七个CSS知识点

如果你在日常工作中使用CSS,那么你的主要目标很可能集中在使事情看起来是正确的。最终得到的正确结果远比如何实现更重要。这意味着相比正确的语法和视觉效果我们更少关注CSS的实现原理。 你可能还没有意识到,但CSS的视觉效果通常是操纵隐藏属性的间接结果。一些CSS属性(如 background-color)和你看到的视觉效果就有显然的直接关系。而其他的(比如display)对我们许多人来说仍然含糊不清,因为结果似乎高度依赖于上下文。 我怀疑很多开发者都不能简单的描述当设置了display: block之后实际上做了什么。最多你可能只是直观地了解这个属性是如何工作的。没关系,你可以在不了解基本原理的情况下,对CSS有很好的使用。虽然,你知道解决问题的方案,但是你却不一定真的了解问题。 对于许多人来说,诸如盒子模型、级联和特殊性等概念是我们所熟知的。虽然他们经常被曲解,但是知道这些工作原理有助于我们编写更好的CSS。 正因为如此,我想试着揭示CSS的这些隐藏黑科技部分,只介绍涉及你需要知道的部分,并希望以逻辑顺序解释该过程,以便你更好的理解CSS真正的工作原理。 渲染过程概述当你加载一个HTML文档的时候,页面的渲染过程中会按照顺序发生了很多事情。 第一步就是解析HTML文档。从这一步开始浏览器会生成一个“文档树”。树的结构是一种用HTML代表具有明显层次结构信息的方式。树中的元素可以用类似于家谱的方式描述,比如:后代节点、父节点、子孙节点和兄弟节点。 你可能听说过“DOM”这个术语。它代表“文档对象模型(Document Object Model)”。它是文档树结构的扩展,它被用来存储web文档内容和操作信息。 随着HTML被解析,样式文件和其他资源文件会被下载。样式声明通过一个称为级联的过程来解释和决定。 在此过程中,将解析CSS属性的最终值。经过计算,这些值可能与我们样式表中所写的值不同。例如:像auto 这样的相对单位的关键字被赋予了真正的值,并会应用继承的值。这些计算好的值会像存储在DOM树中的元素一样被存储在一个树中,毫无疑问会被称为CSS对象模型(CSS Object Model )或CSSOM。 现在就可以开始渲染页面的过程了。这个过程中的第一步是计算 盒模型。这一步对于计算出元素的大小和间距是很重要的,尽管可能并不是最终的位置。 和 盒模型相比并不是那么被熟知的过程叫做 视觉格式模型。此过程会确定页面上元素的布局和位置。它包含您可能已经熟悉的一些概念,例如:定位方案(positioning schemes), 格式化上下文(formatting contexts), 显示模式(display modes), 和 堆叠上下文(stacking contexts)。 最终页面被渲染出来。 以上段落中可能有几个术语,您还不熟悉。如果是这样的话,最重要的是理解 级联, 盒模型, 和 视觉格式模型,理解这些术语是解释、处理和渲染HTML和CSS至关重要的一步。在描述上面每个渲染过程的细节时,我跳过了很多的细节,所以接下来我们更加仔细地看这三个步骤。 级联级联可能是CSS中最容易被弄错的属性之一。它指的是合并不同样式表并解决CSS选择器之间冲突的过程。 级联查看声明的优先级、来源、特性和顺序,以确定使用哪种样式规则。 你需要知道什么: 大多数网站都有多种样式表。通常的样式是在页面中添加了一个引用css文件的link 标签,或者在HTML主体中使用 style 标签。即使最基本的页面也有由浏览器提供的默认样式。此默认样式表有时称为用户代理样式表(user-agent stylesheet)。 在级联过程中样式表按以下顺序解释: !important 声明 开发者编写的样式表 浏览器默认的样式表 注意事项: 我跳过了用户样式表,因为它已不是常见的了。 合并这些来源的样式之后,如果很多规则被用在了同一个元素上,则用权重确定应用的规则。 特殊性特殊性是指选择器的权重。仅仅把它看作一个单独的数字是一个常见的错误。实际上它是4个独立的数字或4种类别的权重。 计算特殊性,把下面的权重相加: ID,class, 属性 和 伪类,元素 和 伪元素例如: #nav .selected:hover > a::before 的权重分别是 1, 2, 2。 ...

May 6, 2019 · 1 min · jiezi

CSS布局基础三栏布局

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

May 5, 2019 · 2 min · jiezi

CSS垂直居中的七个方法

我们在编辑一个版面,通常都会用到水平居中和垂直居中来设计,而水平居中很好处理,不外乎就是设定margin:0 auto;或是text-align:center;,就可以轻松解决掉水平居中的问题,但一直以来最麻烦对齐问题,都是“垂直居中”这个讨人厌的设定,以下将介绍七种单纯利用CSS垂直居中的方式。 七种垂直居中的方法设定行高(line-height)添加伪元素calc动态计算使用表格或假装表格transform绝对定位使用Flexbox设定行高(line-height)设定行高是垂直居中最简单的方式,适用于“单行”的“行内元素”(inline、inline-block),例如单行的标题,或是已经设为inline-block属性的div,若将line-height设成和高度一样的数值,则内容的行内元素就会被垂直居中,因为是行高,所以会在行内元素的上下都加上行高的1/2,所以就垂直居中了!不过由此就可以看出,为什么必须要单行的行内元素,因为如果多行,第二行与第一行的间距会变超大,就不是我们所期望的效果了。CSS示例: .div0{ width:200px; height:150px; border:1px solid #000; line-height:150px; text-align:center;}.redbox{ display:inline-block; width:30px; height:30px; background:#c00;}添加伪元素(::before、::after)刚刚第一种方法,虽然是最简单的方法(适用于单行标题),不过就是只能单行,所以我们如果要让多行的元素也可以垂直居中,就必须要使用伪元素的方式。在此之前,先解释一下CSS里头vertical-align这个属性,这个属性虽然是垂直居中,不过却是指在元素内的所有元素垂直位置互相居中,并不是相对于外框的高度垂直居中。(下面的CSS会造成这种样子的垂直居中) .div0{ width:200px; height:150px; border:1px solid #000; text-align:center;}.redbox{ width:30px; height:30px; background:#c00; display:inline-block; vertical-align:middle;}.greenbox{ width:30px; height:60px; background:#0c0; display:inline-block; vertical-align:middle;}.bluebox{ width:30px; height:40px; background:#00f; display:inline-block; vertical-align:middle;}因此,如果有一个方块变成了高度100%,那么其他的方块就会真正的垂直居中。 .greenbox{ width:30px; height:100%; background:#0c0; display:inline-block; vertical-align:middle;}但是我们总不能每次要垂直居中,都要添加一个奇怪的div在里头吧!所以我们就要把脑筋动到“伪元素”身上,利用::before和::after添加div进到杠杠内,让这个“伪”div的高度100%,就可以轻松地让其他的div都居中。不过不过不过!div记得要把display设为inline-block,毕竟vertical-align:middle;是针对行内元素,div本身是block,所以必须要做更改! .div0::before{ content:''; width:0; height:100%; display:inline-block; position:relative; vertical-align:middle; background:#f00;}calc动态计算看到这边或许会有疑问,如果今天我的div必须要是block,我该怎么让它垂直居中呢?这时候就必须用到CSS特有的calc动态计算的能力,我们只要让要居中的div的top属性,与上方的距离是“50%的外框高度+ 50%的div高度”,就可以做到垂直居中,至于为什么不用margin-top,因为margin相对的是水平宽度,必须要用top才会正确。 .div0{ width:200px; height:150px; border:1px solid #000;}.redbox{ width:30px; height:30px; background:#c00; float:left; top:calc(50% - 15px); margin-left:calc(50% - 45px);}.greenbox{ width:30px; height:80px; background:#0c0; float:left; top:calc(50% - 40px);}.bluebox{ width:30px; height:40px; background:#00f; float:left; top:calc(50% - 20px);}使用表格或假装表格或许有些人会发现,在表格这个HTML里面常用的DOM里头,要实现垂直居中是相当容易的,只需要下一行vertical-align:middle就可以,为什么呢?最主要的原因就在于table的display是table,而td的display是table-cell,所以我们除了直接使用表格之外,也可以将要垂直居中元素的父元素的display改为table-cell,就可以轻松达成,不过修改display有时候也会造成其他样式属性的连动影响,需要比较小心使用。HTML: ...

May 5, 2019 · 1 min · jiezi

关于CSS-Transition你需要知道的事

CSS3的过渡属性,给web应用带来了简单优雅的动画,但是比起初次相见,他(transition)有许多细则。 在这片文章中,我将会专研CSS3的过渡(transition)中更加复杂的部分,从链式和事件到硬件加速和动画函数。 让浏览器控制动画序列,通过改变帧率,减少绘画和减少GPU的工作,能够优化性能和效率。 应用 transition一个最简单使用transition的方法就是和CSS伪元素一起用,比如:hover。注意我们在指定属性名字,transition的时长,以及默计时函数,linear。 .element { height: 100px; transition: height 2s linear;}.element:hover { height: 200px;} 当:hover伪元素被激活的时候,这高度会动态地在两秒内从100px过度到200px。 duration是唯一在transition缩写中需要的项目。浏览器默认的定时方法是ease,以及一个all的属性,除非他们已经提供了。 当谈论到激活transition,我们不希望被限制于使用伪元素 —— 很显然这不灵活。解决这个的方法就是用程序添加和删除class /* CSS */.element { opacity: 0.0; transform: scale(0.95) translate3d(0,100%,0); transition: transform 400ms ease, opacity 400ms ease;}.element.active { opacity: 1.0; transform: scale(1.0) translate3d(0,0,0);}.element.inactive { opacity: 0.0; transform: scale(1) translate3d(0,0,0);}// JS with jQueryvar active = function(){ $('.element').removeClass('inactive').addClass('active');};var inactive = function(){ $('.element').removeClass('active').addClass('inactive');}; 以上的列子,我们用了2个不同的过渡(transition),当激活的时候,元素向上滑动,当无效化之后,淡出。所有的javascript所做的事就是切换active 和 inactive这两个class。 过渡渐变不是所有的CSS属性都能过渡,最基本的规则是你只能过渡绝对值。比如,你不能让height从 0px过渡到auto,浏览器不能计算中间过度值,因此属性变化是瞬间的。Oli Studholme提供了便利的 一份完全过度属性的列表。 ...

May 5, 2019 · 2 min · jiezi

否定伪类nots-CSSTricks

:not(X)是CSS中的一个否定伪类(选择器),并且接受一个简单的选择器作为参数。本质上,可以使任一其他选择器(作为参数)。 :not(选择器)匹配传递参数选择器未选择的元素。传递参数或许不包括增加的选择器或者伪元素选择器。 /* the X argument can be replaced with any simple selectors */:not(X) { property: value;} 在这个例子中,有一个class为“different”的li元素: <ul> <li></li> <li class="different"></li> <li></li></ul> CSS将会选择除了class为“different”外的所有li元素。 /* Style everything but the .different class */li:not(.different) { font-size: 3em;} 可以将伪类选择器应用到所有简单选择器(包括元素类型选择器、通用选择器、属性选择器、类选择器、ID选择器、伪类选择器)上来产生同样的效果。 p:not(:nth-child(2n+1)) { font-size: 3em;} 但是如果我们使用伪元素选择器作为参数将不会产生我们预期的效果。 :not(::first-line) { /* ::first-line is a pseudo element selector and not a simple selector */ color: white;} :not()多种用法可视化表示图 :not()伪类的优先级是其参数的优先级。:not()伪类并不会像其他伪类选择器那样给选择器增加优先级。 否定伪类选择器不支持嵌套,所以:not(:not(...))是永远不被允许的。开发者需要注意伪元素不是简单选择器(simple selector),因此作为:not()伪类的参数是无效的。另外,当使用属性选择器时也需要注意,因为部分属性选择器不被普遍支持。在:not()选择器下链式使用另一个:not()选择器也是禁止的。 学习更多的CSS技术可以关注我的博客:CODECOLOR

April 30, 2019 · 1 min · jiezi

关于CSS中的背景属性background简述

像我之前提到的那样,文档树中的每个元素只是一个矩形盒子。这些盒子都有一个背景层,背景层可以是完全透明或者其它颜色,也可以是一张图片。此背景层由8个CSS属性(加上1个简写的属性)控制。 background-colorbackground-color属性设置元素的背景颜色。它的值可以是任意合法的颜色值或者是transparent关键字。 .left { background-color: #ffdb3a; }.middle { background-color: #67b3dd; }.right { background-color: transparent; } 背景颜色绘制在由[background-clip](#backgroundclip)属性指定的盒模型的区域内。如果还设置了任何背景图像,则在它们后面绘制颜色层。与可以有多个的图像层不同,对于一个元素,我们只能有一个颜色层。 background-imagebackground-image属性定义元素的一个或多个背景图像。它的值通常是用url()符号定义的图像的url。也可以使用none作为它的值,但这样会生成一个空的背景层 .left { background-image: url('ire.png'); }.right { background-image: none; } 我们也可以指定多张背景图片并通过逗号分隔。后面的图片都会绘制在Z轴方向上前一个图片的后面。 .middle { background-image: url('khaled.png'), url('ire.png'); /* Other styles */ background-repeat: no-repeat; background-size: 100px;} background-repeatbackground-repeat属性控制背景图片在被[background-size](#backgroundsize)属性改变了大小及被[background-position](#backgroundposition )属性定位后如何平铺。 该属性的值可以是 repeat-x, repeat-y, repeat, space, round, no-repeat关键字,除了repeat-x和repeat-y,其他值可以为x轴和y轴定义一次,也可以单独定义每个维。 .top-outer-left { background-repeat: repeat-x; }.top-inner-left { background-repeat: repeat-y; }.top-inner-right { background-repeat: repeat; }.top-outer-right { background-repeat: space; }.bottom-outer-left { background-repeat: round; }.bottom-inner-left { background-repeat: no-repeat; }.bottom-inner-right { background-repeat: space repeat; }.bottom-outer-right { background-repeat: round space; } ...

April 30, 2019 · 2 min · jiezi

前端开发中遇到的一些问题持续更新

页面两个div之间有一个小margin,样式怎么改都去不掉解决:html中div换行了,把两个div放到同一行可以解决,还有一种好办法就是使用flex。 app上点击有背景解决:全局添加样式-webkit-tap-highlight-color: rgba(255, 255, 255, 0); 手机端input边框阴影解决:添加-webkit-appearance: none; chrome模拟器里点击元素错位解决:我把百分比改成fit to window 好了,也可以来回切换几次机型。 git push 报错RPC failed; curl 55 SSLWrite() returned error -9805解决:一次性提交代码太多导致,使用git log找到最近一次commit的代码,git reset {id}回退到commit前的状态,再分批次提交。 代码丢失,git log找不到解决:git reset --hard xxx的记录,使用git reflog。 sudo nginx -s reload启动nginx报错:nginx: [error] open() "/usr/local/var/run/nginx.pid" failed (2: No such file or directory) 解决:sudo nginx -c /usr/local/etc/nginx/nginx.conf => sudo nginx -s reload input中type为number时maxlength失效解决:input type="tel" 微信授权callback有多个参数,出现丢失的情况。解决:因为授权接口中&的多个参数会变成整个接口地址的参数,所以需要对&进行加密,使用var callbackurl = encodeURIComponent(window.location.href)进行加密 滚动不顺畅解决:添加样式 -webkit-overflow-scrolling: touch;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;事件冒泡解决: stopPropagation,cancelBubble(ie) 移入移出多次触发解决: mouseleave 对子元素不生效,不会冒泡 ...

April 29, 2019 · 2 min · jiezi

基础这15种CSS居中的方式你都用过哪几种

简言CSS居中是前端工程师经常要面对的问题,也是基本技能之一。今天有时间把CSS居中的方案汇编整理了一下,目前包括水平居中,垂直居中及水平垂直居中方案共15种。如有漏掉的,还会陆续的补充进来,算做是一个备忘录吧。 1 水平居中1.1 内联元素水平居中利用 text-align: center 可以实现在块级元素内部的内联元素水平居中。此方法对内联元素(inline), 内联块(inline-block), 内联表(inline-table), inline-flex元素水平居中都有效。 核心代码: .center-text { text-align: center;}演示程序:演示代码 1.2 块级元素水平居中通过把固定宽度块级元素的margin-left和margin-right设成auto,就可以使块级元素水平居中。 核心代码: .center-block { margin: 0 auto;}演示程序:演示代码 1.3 多块级元素水平居中1.3.1 利用inline-block如果一行中有两个或两个以上的块级元素,通过设置块级元素的显示类型为inline-block和父容器的text-align属性从而使多块级元素水平居中。 核心代码: .container { text-align: center;}.inline-block { display: inline-block;}演示程序:演示代码 1.3.2 利用display: flex利用弹性布局(flex),实现水平居中,其中justify-content 用于设置弹性盒子元素在主轴(横轴)方向上的对齐方式,本例中设置子元素水平居中显示。 核心代码: .flex-center { display: flex; justify-content: center;}演示程序:演示代码 2 垂直居中2.1 单行内联(inline-)元素垂直居中通过设置内联元素的高度(height)和行高(line-height)相等,从而使元素垂直居中。 核心代码: #v-box { height: 120px; line-height: 120px;}演示程序:演示代码 2.2 多行元素垂直居中2.2.1 利用表布局(table)利用表布局的vertical-align: middle可以实现子元素的垂直居中。 核心代码: .center-table { display: table;}.v-cell { display: table-cell; vertical-align: middle;}演示程序:演示代码 ...

April 29, 2019 · 2 min · jiezi

单一div的正多边形变换纯CSS

上一篇我们介绍了如何利用before和after伪元素来做Material Design风格的按钮,里头关键的技术就在于活用边框宽度和div本体宽高,因此这篇再加码一个效果,就是纯粹利用CSS,让“单一个”div,从正三角形变换为正八边形(单一div最多只能做到正八边形),最后再搭配动画的效果,变成正多边形的变换动画,也由于正多边形需要用到不少的三角函数计算,为了方便起见,这里将正多边形的边统一都设为100px。 正三角形正三角形不需要用到伪元素,只需要设定div本身的边框宽度即可产生,先来看一下正三角形的边长与中线,若边长为100px,则中线四舍五入就是87px(100 x sin(60)= 87)。 因此我们要将div的长宽都设为0,接着把底部border的宽度设为87px,左右的border宽度设为50px(颜色设为透明transparent),就可以做出一个漂亮的三角形。 width:0;height:0;border-width:0 50px 87px ;border-style:solid;border-color:transparent transparent #095; 正方形正方形应该是最简单的,只要设定长宽设定为同样数值就可以了,不过其实还有另外两种方法,第一种你可以把长宽设为0,把上下左右的border设为50px也可以,第二种则是高度设为0,宽度设为100px,然后某个边宽也设为100,都是可以的。 .a{width:100px;height:100px;background:#c00;}.b{width:0;height:0;border-width:50px;border-style:solid;border-color:#095;}.c{width:100px;height:0;border-width:0 0 100px;border-style:solid;border-color:#069;} 正五边形正五边形就需要进入基本的三角函数领域了,让我们先把正五边形分解,用原本的div作为上方的三角形,然后用一个伪元素制作下方的梯形,因为正五边形每边的夹角为108度,所以可以藉由三角函数计算出上方三角形的高度为59px(100 x cos(54)),宽度为192px(100x sin(54)x 2),下方梯形的高度为95px(100 x sin(72)),长边的宽度跟上面的三角形一样都是192px。 了解原理之后,就可以利用伪元素来搭配制作啰! .a{ position:relative; width:0; height:0; border-width:0 81px 59px; border-style:solid; border-color:transparent transparent #069;}.a:before{ position:absolute; content:""; top:59px; left:-81px; width:100px; height:0; background:none; border-width:95px 31px 0; border-style:solid; border-color:#069 transparent transparent; } 正六边形正六边形的每个夹角是120度,如果以纯CSS的方向来看的话,就是把正五边形上面的三角形改变一下,就可以做出正六边形,也就是变成上下两个梯形的组合而已,梯形的长边为200px(100 x cos(60)x 2 + 100),梯形的高度为87px(100 x sin(60))。 所以只要把正五边形的CSS稍作修改就可以做出正六边形了。 .a{ position:relative; width:100px; height:0; border-width:0 50px 87px; border-style:solid; border-color:transparent transparent #f80;}.a:before{ position:absolute; content:""; top:87px; left:-50px; width:100px; height:0; background:none; border-width:87px 50px 0; border-style:solid; border-color:#f80 transparent transparent; } ...

April 28, 2019 · 1 min · jiezi

送给CSS初学者的悬停过渡动画三部曲

CSS不一定要写得多么复杂才能实现特殊效果。如下就是三个超级简单的过渡的例子,可能只是几行代码,但是添加到Web应用程序中,却会让它增色不少。如下是我们将在本教程中构建的代码 项目设置在这个项目中,我们将把过渡效果应用到一个class为box的元素上面。这个box元素内部是垂直和水平居中的文字内容。HTML结构相当简单: <div class='box'> <p>TEXT</p></div>CSS代码也一样简单。我们想要使用无衬线字体,并确保div中的段落文本是白色的,可以通过如下代码来实现: body { color: white; font-family: Helvetica, Sans-Serif;}另外,给box元素添加如下CSS属性: .box { width:200px; /* Set the Width of box */ height:50px; /* Set the Height of box */ background:#424242; /* Dark Grey Background color */ transition:all 0.25s ease; /* Transition settings */ display: flex; /* Use Flexbox on P */ align-items: center; /* Center P */ justify-content: center; /* Center P */ margin: 10px; /* Apply a margin around our Box */}无论你对CSS的过渡属性熟悉与否,我们在这里都来简要介绍一下,一共分为三步。.第一步,我们需要将它应用到all变化的属性。接下来,设置过渡时长为0.25秒。最后,将动画函数选为ease。ease的表现状态就是起止过程比较缓慢,中间过渡迅速。holly high! 目前准备工作都已经就绪,接下来就是添加过渡效果了。到目前为止,div看起来应该像下面这样。 ...

April 28, 2019 · 1 min · jiezi

纯CSS-Material-Design风格按钮

其实Material Design的扁平化icon按钮,这类型的按钮往往只利用几何色块的变化,就能抓住使用者的眼光,并且从几何形状中明白按钮的含意,这也是Material Design非常强调的设计理念和精髓。 应用重点与原理在这篇我纯粹利用CSS,就做出了Material Design风格的按钮,其实没有很难,比较需要知道的重点如下: 伪元素before和after的应用伪元素在里头扮演相当重要的角色,利用伪元素我们可以产生两个不在HTML里头的类div,可以大幅降低代码的复杂程度。 div的自身宽度与边框由于我们要进行三角形与矩形之间的形变,虽然三角形可由边框产生,但却无法做出漂亮的形变效果(用背景色的话就会有淡入淡出的现象),所以必须要用边框宽度和矩形大小去搭配,当边框变细的时候,矩形变大,如此一来就可以做出三角形与矩形互相变换的效果啰,下图的红色是纯粹用颜色变换,蓝色则是用边框宽度与div宽度变换,效果应该很明显吧! transform的应用CSS里所有的形变都是藉由transform来完成(必要时请加各个浏览器的前坠字),示例里头会用到scale与rotate这两个变形属性。 实现Material Design风格按钮了解原理之后,先来看一下HTML代码,代码里面有两个主要的div分别是a和b,a的话是利用伪元素来进行变换,b的内容还有三个小i分别是b1、b2和b3(都会宣告为block属性),因为大于两个就无法纯粹使用伪元素,所以直接用三个block元素来表示比较快。 <div class="a"></div><div class="b"> <i class="b1"></i> <i class="b2"></i> <i class="b3"></i></div>接下来就是CSS了,先看到a,首先当然是先画两条垂直的矩形,做出暂停的icon,这里直接利用伪元素来画,比较特别的是「高度为0」,因为在上面有说过,为了要塑造一个「形状的变换」,而不是「颜色的淡入淡出」,所以必须用border-width来代替高度(记得加上transition的渐变时间)。 .a{ position:absolute; top:50px; left:50px; width:100px; height:100px; border-radius:50%; background:#363; transition:.2s;}.a:before,.a:after{ content:""; position:absolute; width:12px; height:0; top:24px; border-style:solid; border-width:0 0 54px 0;}.a:before{ left:27px; border-color:#fff rgba(255,255,255,0) #fff rgba(255,255,255,0); transition:.2s;}.a:after{ left:54px; border-color: rgba(255,255,255,0) rgba(255,255,255,0) #fff #fff ; transition:.2s;}主体设定好之后,接着就要来设定hover和active的效果,这里就会用transform的scale和rotate,除了变形,仔细看一下,宽度和border宽度都改变了,加上位置的互相搭配,就可以很容易地做出两个矩形在鼠标移上去的时候变成三角形,点下去的时候变成正方形啰!(scale如果设定两个值,分别就是宽与长的变形比例) .a:hover:before{ top:26px; left:45px; width:0; transform:scale(2,1.17) rotate(90deg); border-width:0 0 24px 24px; }.a:hover:after{ top:53px; left:45px; width:0; transform:scale(2,1.17) rotate(90deg); border-width:0 24px 24px 0; }.a:hover{ background:#095; transition:.4s;}.a:active:before{ border-width:0 0 24px 0; width:22px; top:26px; left:38px; transition:.4s;}.a:active:after{ border-width:0 0 24px 0; width:22px; top:50px; left:38px; transition:.4s;}.a:active{ transform:rotate(180deg); background:#0a9;}完成的效果就是长这样。 ...

April 26, 2019 · 1 min · jiezi

CSS变量自定义属性使用指南-SitePoint

CSS预处理器,如Sass和Less,使得CSS代码易于组织和维护。通过提供变量、混合、循环等特性,使得CSS具有动态编写的能力,从而减少重复性工作,提高开发速度。 最近,CSS开始添加一些动态特性。CSS变量(自定义属性)已经加入规范,并且获得了大多数浏览器的支持。但是CSS混合特性还在进行中。 在这篇文章中,我们将会向你展示怎么把CSS变量应用到开发中,从而使得样式表更加可维护和DRY (Don’t Repeat Yourself)。 让我们现在开始! CSS变量是什么?如果你使用过任何编程语言,你肯定熟悉变量这个概念。变量让你存储和更新程序运行需要的值。 例如,考虑下面的JavaScript片段: let number1 = 2;let number2 = 3;let total = number1 + number2; console.log(total); // 5number1 = 4;total = number1 + number2;console.log(total); // 7number1和number2是两个变量,分别存储数字2和3。 total也是一个变量,存储number1和number2变量的和,在这个例子中是5。你可以动态更新这些变量的值,并且在程序的任何地方使用更新后的值。在上面的代码片段中,我把number1的值更新为4,当我使用相同的变量再次执行加法操作时,存储在total中的值就变成7,而不是5了。 变量的好处在于你可以把值存储在一个地方,然后在你需要的地方修改它。这样你就不用在程序的不同地方为不同的值添加不同的变量:所有变量更新使用同一个存储地址,比如你的变量。 CSS主要是一门声明式语言,缺乏动态性。你可能会说给CSS添加变量会与CSS本身相矛盾。如果前端开发仅仅关注语义,那么给CSS添加变量确实会与CSS本身矛盾。幸运的是,网络语言更像动态语言,它会随着周围环境和开发者的需求不断变化。CSS也不例外。 总而言之,变量已经成为CSS中令人激动的实现,你很快也会发现,学习和使用它非常直观。 使用CSS变量有什么好处?在CSS中使用变量的好处和在编程语言中没有特别大的不同。 下面是规范对上述问题的回答: [使用CSS变量]使大文件更易于阅读,因为看起来很随意的值有了一个提示信息的名字,并且编辑这些文件更加简单,更不易于出错。因为你只需要在自定义属性处修改一次,然后这个修改就会自动应用到使用该变量的任何地方。W3C规范.换句话说:通过与项目相关的方式命名变量,管理和维护代码会变得更加容易。例如,如果项目的主色调保存在--primary-color中,修改项目的主色调就会变得很容易,仅仅改变该变量的值就可以,而不用去修改遍布在代码各处、不同CSS属性中的颜色值。 CSS变量和预处理器变量的不同之处?在给网站添加样式时,你可能已经通过预处理器,如Sass和Less,体验过变量的灵活性带来的好处。 预处理器可以让你设置变量,并且在函数、循环和数学操作等中使用。这是不是意味着CSS变量就没有什么用处了? 不完全是,主要是因为CSS变量和预处理器变量并不一样。 不同之处在于CSS变量是运行在浏览器中的动态CSS属性,而预处理器变量会被编译成普通的CSS代码。因此,浏览器并不知道预处理器变量的存在。 这就意味着你可以更改样式表、行内样式属性和SVG展示型属性中的CSS变量,或者使用JavaScript操作它们。这是预处理器变量做不到的。CSS变量提供了更多可能性! 但这并不是说你需要在二者之间选择其一:你可以同时使用CSS变量和预处理器变量的强大功能。 CSS变量的语法为了简单起见,在这篇文章中我使用了CSS变量这个术语,但是官方文档给出的是级联变量的CSS自定义属性。CSS自定义属性形式如下: `--my-cool-background: #73a4f4;`在自定义属性前面添加两个短横线,然后像普通的CSS属性一样给它赋值。在上面的代码片段中,给--my-cool-background自定义属性赋了一个颜色值。 级联变量部分包括使用var()函数应用自定义属性,形式如下: `var(--my-cool-background)`自定义属性的使用范围是CSS选择器的内部,var()像一个真正的CSS属性值被使用。 :root { --my-cool-background: #73a4f4;}/* CSS文件的其他部分 */#foo { background-color: var(--my-cool-background);}上面的代码片段把--my-cool-background自定义属性定义在:root伪元素内,这使得自定义属性的值全局可用(:root匹配<html>元素内的任何元素)。然后使用var()函数把值应用到ID是foo的容器的background-color属性上,然后这个容器就会得到一个淡蓝色背景。 除此之外,还可以把淡蓝色应用到多个HTML元素的其他颜色属性上,如color,border-color等。你需要做得仅仅是通过var(--my-cool-background)获取自定义属性的值,然后应用到相应的属性上。当然,你需要好好考虑CSS变量的命名规范,使你的变量名能更好地反映变量的内容。 p { color: var(--my-cool-background);}查看CodePen上SitePoint(@SitePoint)的CSS变量运行实例。 你也可以在CSS变量中使用另一个CSS变量,举例如下: --top-color: orange;--bottom-color: yellow;--my-gradient: linear-gradient(var(--top-color), var(--bottom-color));上面的代码片段创建了--my-gradient变量,它的值是使用--top-color和--bottom-color变量创建的一个渐变。现在,你可以在任何地方通过仅仅改变变量的值来修改渐变,而不必到处在样式表中创建渐变实例。 ...

April 26, 2019 · 2 min · jiezi

利用CSS变量实现炫酷的悬浮效果

最近,我从 Grover网站上发现以一个好玩儿的悬停动画,也有了些自己的灵感。这个动画是将鼠标移动到订阅按钮上移动光标会显示相应的彩色渐变。这个想法很简单,但是它能使这个按钮脱颖而出,人们一下子就注意到它了,增加了点击的概率。怎样才能达到这个效果,使我们的网站脱颖而出呢?其实,它并不像你想象的那么难! 追踪位置我们要做的第一件事就是获取到鼠标的位置。 document.querySelector('.button').onmousemove = (e) => { const x = e.pageX - e.target.offsetLeft const y = e.pageY - e.target.offsetTop e.target.style.setProperty('--x', `${ x }px`) e.target.style.setProperty('--y', `${ y }px`)}选择元素,等待,直到用户将鼠标移过它;计算相对于元素的位置;将坐标存在CSS的变量中。是的,仅仅9行代码就让你能获知用户放置鼠标的位置,通过这个信息你能达到意想不到的效果,但是我们还是先来完成CSS部分的代码。 动画渐变我们先将坐标存储在CSS变量中,以便能够随时使用它们。 .button { position: relative; appearance: none; background: #f72359; padding: 1em 2em; border: none; color: white; font-size: 1.2em; cursor: pointer; outline: none; overflow: hidden; border-radius: 100px; span { position: relative; } &::before { --size: 0; content: ''; position: absolute; left: var(--x); top: var(--y); width: var(--size); height: var(--size); background: radial-gradient(circle closest-side, #4405f7, transparent); transform: translate(-50%, -50%); transition: width .2s ease, height .2s ease; } &:hover::before { --size: 400px; }}用span包裹文本,以避免显示在按钮的上方。将 width和height初始化为0px,当用户悬停在按钮上时,将其改为400px。不要忘了设置这种转换以使其像风一样????瞬间出现;利用坐标追踪鼠标位置;在background 属性上应用 radial-gradient,使用closest-side circle。Closest-side能够覆盖整个面。结果成功啦!将其加入到对于的HTML页面,你炫酷的按钮就可以使用啦! ...

April 25, 2019 · 1 min · jiezi

flex布局中某个行内元素居右并垂直居中

首先父盒子设置成display: flex子元素(行内元素)设flex: 1和text-align: right<div class="flex-box"> <div>1</div> <div>2</div> <div>3</div> <span class="item-right">水平居右并垂直居中</span> </div>.flex-box { display: flex; flex-direction: row;}.flex-box div { width: 100px; height: 100px; background: #f9f9f9; margin-right: 10px;}.item-right { flex: 1; text-align: right; align-self: center;}完整的HTML如下: <!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> .flex-box { display: flex; flex-direction: row; } .flex-box div { width: 100px; height: 100px; background: #f9f9f9; margin-right: 10px; } .item-right { flex: 1; text-align: right; align-self: center; } </style></head><body> <div class="flex-box"> <div>1</div> <div>2</div> <div>3</div> <span class="item-right">水平居右并垂直居中</span> </div></body></html>

April 25, 2019 · 1 min · jiezi

'align-items: center/flex-end ' breaks 'overflow: scroll'

实例<div style="width: 200px; position:relative; top: 200px; background: silver;"> <div class="slidesWrap" style="height:200px"> <div style="height:300px; width:100%; background: pink;">content</div> <div style="height:30px; width:100%; background: green;">content</div> </div></div>.slidesWrap { display: flex; align-items: center; overflow: auto;}.slide { overflow: auto; flex: 1; max-height:100%;}结果: 左侧区域的content不见了。而且滚动也不会出现。这是因为overflow:scroll 只会对下方或右侧超出的部分滚动 ,对左侧和上方无效(左侧可以尝试float: right设置超出。也是横向无滚动) 解决方案1.中间再包一层2.max-height:100%; 实例: <div style="width: 200px; position:relative; top: 200px; background: silver;"> <div class="slidesWrap" style="height:200px"> <div class="slide"> <div style="height:300px; width:100%; background: pink;">content</div> </div> <div class="slide"> <div style="height:30px; width:100%; background: green;">content</div> </div> </div></div>.slidesWrap { display: flex; flex-direction: row; align-items: flex-end;}.slide { overflow: auto; flex: 1; max-height:100%;}结果: ...

April 22, 2019 · 1 min · jiezi

CSS 实现滚动时隐藏滚动进度条

CSS3 代码html { -ms-overflow-style:none; overflow:-moz-scrollbars-none; }html::-webkit-scrollbar{ width:0px}↑ 支持 Firefox、Webkit 内核的浏览器

April 21, 2019 · 1 min · jiezi

运用clip-path的纯CSS形状变换

在之前所提到绘制正多边形的方法,算是纯粹利用伪元素来完成,不过坦白说还有另外一种方法,可以将单一div做更多形状的变换,这种方法就是CSS3的“clip-path”,这个“clip-path”看起来有点眼熟,因为它原本就存在于SVG里头,利用掩码(剪裁)的方法,连接坐标绘制掩码区域,就可以做出许多不同的形状,让底色或底图显现,随着浏览器对于CSS3的支持度大幅提升,自然而然的就可以用它来做些与众不同的变化。运用clip-path超强的网站最先看到这个属性的应用,是从这个网站看到的:http://species-in-pieces.com/,不得不说这个网站做得实在太神了,一开始看到还真以为是用SVG做的,没想到竟然是用CSS刻出来的…真是太惊人啦!接着在找数据的过程中,又发现一个很强的网站:http://bennettfeely.com/clippy/,专门介绍「clip-path」这个CSS3的属性,你可以直接在上面拖拉或修改,除了贝兹曲线外,几乎任何形状都做得出来(中空的部分要用组合的)使用clip-path绘制多边形如果不考虑一些浏览器支持度的问题,使用clip-path来绘制多边形,还比利用伪元素还制作多边形来得简单许多,而且也可以做到单一div绘制超过八边形,使用伪元素的绘制是直接从长宽着手,而利用clip-path则是要由每个点的坐标着手,因为是座标点的缘故,要做出正多边形就也同样要用到许多基本的三角函式来计算坐标,以下就利用clip-path来绘制圆形、椭圆和正多边形给大家看看。开始绘制之前,有两点注意事项:使用clip-path要从同一个方向绘制,如果顺时针绘制就一律顺时针,逆时针就一律逆时针,因为polygon是一个连续的线段,若线段彼此有交集,面积区域就会有相减的状况发生(当然如果这是你要的效果就无妨了)。如果绘制时采用「比例」的方式绘制,长宽就必须要先行设定,不然有可能绘制出来的长宽和我们想像的就会有落差,使用「像素」绘制就没有这种问题。圆形circle(半径at圆心坐标).circle{ width:100px; height:100px; background:#0cc; -webkit-clip-path:circle(50% at 50% 50%); }椭圆形ellipse(长、短轴半径at圆心坐标) .ellipse{ width:100px; height:100px; background:#aaa; -webkit-clip-path:ellipse(25% 50% at 50% 50%); }内置矩形inset(上右下左的边距round上右下左圆角).inset{ width:100px; height:100px; background:#99f; -webkit-clip-path:inset(10px 20px 30px 10px round 20px 5px 50px 0); }正三角形 .a{ width:100px; height:87px; background:#c00; -webkit-clip-path:polygon(0% 100%, 50% 0%,100% 100%); }正方形.b{ width:100px; height:100px; background:#069; -webkit-clip-path:polygon(0% 0%, 0% 100%,100% 100%,100% 0%); }正五边形正五边形就要计算一下了,59/(59+95)=38.31%,31/(81*2)=19.14%.c{ width:162px; height:154px; background:#095; -webkit-clip-path:polygon(0% 38.31%, 50% 0%,100% 38.31%,80.86% 100%,19.14% 100%); }正六边形正六边形的计算比较简单,50/(100+502)=25%,150/(100+502)=75%.d{ width:200px; height:174px; background:#f80; -webkit-clip-path:polygon(25% 0%, 75% 0%,100% 50%,75% 100%,25% 100%,0% 50%); }正七边形正七边形是这里头需要计算最多的形状,22/(100+622)=10.09%,202/(100+622)=90.18%,43/(43+97+78)=19.72%,(43+97)/(43+97+78)=64.22%,62/(100+622)=27.68%,(100+62)/(100+622)=72.32%.e{ width:224px; height:218px; background:#09c; -webkit-clip-path:polygon(50% 0%, 90.18% 19.72%,100% 64.22%,72.32% 100%,27.68% 100%,0% 64.22%,10.09% 19.72%); }正八边形正八边形的计算如下,71/(100+712)=29.34%,(71+100)/(100+712)=70.66%.f{ width:242px; height:242px; background:#f69; -webkit-clip-path:polygon(29.34% 0%, 70.66% 0%,100% 29.34%,100% 70.66%,70.66% 100%,29.34% 100%,0% 70.66%,0% 29.34%); }搭配clip-path做动画熟练了clip-path之后,当然就要用它来做点动画,下面提供两个示例,第一个是正多边形的变换,第二个则是三角形的变换组合,虽然看起来很简单,但实际制作起来却要考虑每个点的坐标,为了让形状不要有「翻转」的错觉,形状里头每个点在进行移动的时候,尽可能的不要交错,因为只要一交错,就会有交集的空白产生,就会有不自然或是翻转的现象喔!当然同样的,如果你希望有翻转的效果,应该就要让他们交错啰!正多边形的变换三角形的变换组合需要以上两个动画的代码可以加我: ...

April 19, 2019 · 1 min · jiezi

玩转CSS 3D -正四面体与正六面体

我们理解了CSS 3D的个中原理之后,废话就不用多说,直接来画正多面体吧!只要正多面体可以画出来,基本上在CSS 3D的领域里,大概就没甚么难得倒我们了。首先看一下百度百科对于正多面体的介绍:“多面体,或称柏拉图立体,指各面都是全等的正多边形且每一个顶点所接的面数都是一样的凸多面体。”简单来说,就是非常对称的立方体,而且每一个面都是由正多边形组成,因此在这一篇,将会画出正四面体、正六面体。正六面体要绘制正多面体,第一个一定要先画正六面体,为什么呢?因为正六面体就是我们熟知的正立方体,夹角都是90度,也是最容易理解的形状(就算有写少许错,好像还是画得出来…)首先我们要先在space里头放入六个正方形,分别给予box1到box6的类别名称识别,并写上1到6来确定翻转是否为正面朝外(朝内的话文字就会是反过来的)。<div class=“camera”> <div class=“space”> <div class=“box1”>1</div> <div class=“box2”>2</div> <div class=“box3”>3</div> <div class=“box4”>4</div> <div class=“box5”>5</div> <div class=“box6”>6</div> </div></div>接着对camera、space和这些box做基本的CSS定义,记得box的position要设为absolute,才不会互相挤压。.camera{ width:200px; height:200px; perspective-origin:center center; -moz-perspective-origin:center center; -webkit-perspective-origin:center center; perspective:500px; -moz-perspective:500px; -webkit-perspective:500px;}.space{ position:relative; width:100%; height:100%; border:1px dashed #000; transform-style:preserve-3d; -moz-transform-style:preserve-3d; -webkit-transform-style:preserve-3d;}.space div{ position:absolute; width:100px; height:100px; font-size:50px; text-align:center; line-height:100px;}完成后先来看box1,box1最简单,只要将它位移到space的中间即可。.box1{ background:rgba(255,0,0,.2); transform:translateX(50px) translateY(50px);}box2除了移到中间,还必须要旋转90度,这时候就要使用transform-origin的属性,这可以设定物体作变化时要以自身的哪一点为主,设定不同的位置,旋转90度之后就会在不同的位置,这里我们先把box2水平位移150px,然后把变换的X定位在左侧,旋转后就会可以顺利地接在box1旁边。.box2{ background:rgba(255,255,0,.2); transform-origin:left top; transform:translateX(150px) translateY(50px) rotateY(90deg);}box3刚好在box1的正对面,所以只要旋转180度即可,但很重要的是旋转之后整个Z轴会跟着旋转180度,为了避免错乱,先把box3往Z轴后面移动100px,旋转后就会正常。.box3{ background:rgba(0,255,0,.2); transform:translateX(50px) translateY(50px) translateZ(-100px) rotateY(180deg);}box4和box2类似,不过要将旋转的中心点移到右侧,也因为移到右侧的缘故,所以我们要先进行X的位移。.box4{ background:rgba(255,0,255,.2); transform-origin:right top; transform:translateX(-50px) translateY(50px) rotateY(-90deg);}box5在上方,box6在下方,用跟box1到box4同样的方法,只是这次是要绕着X轴旋转。.box5{ background:rgba(0,0,255,.2); transform-origin:center bottom; transform:translateX(50px) translateY(-50px) rotateX(90deg);}.box6{ background:rgba(0,255,255,.2); transform-origin:center top; transform:translateX(50px) translateY(150px) rotateX(-90deg);}完成之后应该就会顺利地看到一个正六面体,这时候我们可以改变camera的perspective,可以更加清楚。我们可以在space套用animate的效果,透过space的旋转,仿佛就是一个正立方体在旋转啰!.space{ position:relative; width:100%; height:100%; -webkit-transform-style:preserve-3d; -webkit-transform-origin:center center -50px; -webkit-animation:s 4s linear infinite;}@-webkit-keyframes s{ 0%{ -webkit-transform:rotateY(0); } 100%{ -webkit-transform:rotateY(-359.9deg); }}正四面体理解正六面体之后,正四面体就比较好上手了,正四面体由四个正三角形组成,每个面之间的夹角为70.5度,所以待会旋转的角度也就是70.5度。首先看到HTML的结构就是只有四个div而已。<div class=“camera”> <div class=“space”> <div class=“box1”></div> <div class=“box2”></div> <div class=“box3”></div> <div class=“box4”></div> </div></div>camera和space的设定就不多谈,直接看到这些box的属性,别忘记要画正三角形,就要用到边框来绘制。.space div{ position:absolute; width:0; height:0; border-width:0 50px 87px; border-style:solid; opacity:.4;}首先看到box1,box1依旧是最简单的(但也是比正方形的麻烦一点),这里我要用box1当底,所以除了直接做位置的移动,移动后还要绕X轴旋转90度,比较特别的是因为正三角形是由边框构成,所以颜色就是要改变border才可以。.box1{ border-color:transparent transparent #f00; transform-origin:center bottom; transform:translateX(50px) translateY(50px) rotateX(-90deg);}再来就是另外三个面了,box2跟box1差不多简单,只是box1旋转90度,box2要旋转19.5度,因为box1要和box2有着70.5度的夹角。.box2{ border-color:transparent transparent #00f; transform-origin:center bottom; transform:translateX(50px) translateY(50px) rotateX(-19.5deg);}box3比较麻烦,我们要先让box3绕着Y轴旋转60度(因为正三角形每个角是60度),绕完之后,再绕X轴旋转19.5度,为什么这里的19.5度是正值呢?因为我们绕Y轴旋转后,X轴也跟着旋转,所以绕X轴的方向就变成颠倒过来了。.box3{ border-color:transparent transparent #00f; transform-origin:right bottom; transform:translateX(50px) translateY(50px) rotateY(60deg) rotateX(19.5deg);}box4和box3类似,同样的要先绕Y旋转60度,这次是要绕负的,因为是另外一侧。.box4{ border-color:transparent transparent #f0f; transform-origin:left bottom; transform:translateX(50px) translateY(50px) rotateY(-60deg) rotateX(19.5deg);}按照上面做,应该就可以得到下图的结果:同样的,把space加上动画,就可以看到正四面体旋转啰!比较不同的地方是中心点的位移为29px,为什么呢?因为要抓取正三角形的中心点,数学式为:tan(30deg)x 50 = 28.86。.space{ position:relative; width:100%; height:100%; transform-style:preserve-3d; -webkit-transform-origin:center center 29px; -webkit-animation:s 4s linear infinite;}@-webkit-keyframes s{ 0%{ -webkit-transform:rotateY(0); } 100%{ -webkit-transform:rotateY(-359.9deg); }} ...

April 19, 2019 · 1 min · jiezi

CSS技巧之'text-justify'

CSS中,当text-align属性被赋值为justify时,text-justify属性经常被用来和text-align属性一起设置文本两端对齐的方式。p { text-align: justify; text-justify: inter-word;}Valuesinter-word: 表示当前文本是通过调整单词(word)之间的间隔来实现两端对齐的,实际上是增加了多余的单词间距.这个属性值其实是word-spacing 属性的变形.inter-character: 表示当前文本是通过调整字符(character)之间的间隔来实现两端对齐的,实际上是增加了多余的字符间距.这个属性值其实是letter-spacing 属性的变形.auto: 允许浏览器从inter-word 和inter-character中挑选合适的值作为两端对齐的对齐方式.多语言情景下,文本渲染之前无法得知这是哪一国的语言.这个时候,(auto)就会允许浏览器用户代理来根据当前文本的语言来选择适合的两端对齐方式.none: 禁用两端对齐规则,也就是移除任何可能通过(样式)层叠生效的两端对齐方式的设置或者重写.到底什么是两端对齐?两端对齐文本是一个很富有想象力的说法,它用来描述文本是如何填充满包含它的父容器的方式.实际上,你可以已经很熟悉两端对齐的文本了,但是你却不认识它.如果你曾经使用过文本编辑软件,比如Word和Google Docs的话,你就可能非常熟悉下面的这些图标了(icons):前面三个是用来设置文本对齐的,就好像CSS的text-align属性, 可以设置文本左对齐、右对齐、和居中对齐.第四个icon就是两端对齐选项.它让文本内容填充满文档的整个宽度,不管有没有影响到单词间距,每一行文字都会紧靠边界右对齐.Google Docs中的两端对齐文本每一行都会扩大单词间距来占据整篇文档的宽度.text-justify 属性允许我们实现同样的效果,但是它可以灵活变通来决定是通过单词(words)间距还是字符(characters)间距来调整文本两端对齐方式.浏览器兼容性text-justify 属性已经被收入了CSS Text Module Level 3 文档。由于可能会在候选推荐期间就被废弃掉,当前text-justify属性已经被列为"at risk"(危险)级别.在不远的将来,它不太可能被纳入各浏览器通用标准,因此并不建议在项目生产中使用这个属性.当前完全支持它的只有Firefox 55+. Internet Explorer 11 和 Edge 14+也支持此属性,但是只有inter-word值有效,而且因为是非正式的属性值,W3C规范也未收录它.浏览器支持数据来自 Caniuse, 点击浏览更多. 有数据表明浏览器版本支持该特性.

April 19, 2019 · 1 min · jiezi

Web真相: CSS不是真正的编程 | Christian Heilmann

每隔几个月就会出现一篇文章表明:CSS并不是真正的编程语言。以编程语言的标准来说,CSS过于困难。使用这门语言会很有创造性:人们对CSS有一些强烈的情愫。— Dave Rupert (@davatron5000) [September 18, 2017]事实确实如此,CSS不同于传统的编程,且具有缺陷,同任何标准化编程语言相比,使用起来都更为困难。这是由于CSS被设计为一种描绘界面的方式,而不是以编程形式实现该界面,例如canvas的API。CSS的设计初衷就不同于传统编程语言。CSS为用户上网时遇到的一些复杂且未知的东西创建界面,这个设计初衷是很棒的。作为一名CSS开发者,你相信用户代理(大部分情况下指的是浏览器)会表现正确的行为。你无法控制CSS发生的时机,但同时你也无需担心性能、渲染时间和响应的具体细节,因为这些细节是由浏览器开发者和浏览器所处操作系统决定的。不过很棒的一点是,CSS允许你在其应用的地方修改这些重要的细节。如果你使用JavaScript来创建界面或动画,你不仅需要做更多深入细致的控制,还要确保一切都能正常工作,否则可能会阻塞页面的正常显示。使用CSS就意味着放弃控制,而去花更多的时间创建友好的响应式交互界面。用户可能会搞乱你的界面设置,但CSS可以为你规避这种情况。使用CSS开发不同于传统模式,并不需要循环、条件和变量。但CSS正朝着这个方向发展,Sass作为CSS的扩展语言,引入了变量,为CSS未来的发展奠定了基础。但CSS最需要的不是语法,而是你要清楚使用CSS所描绘的界面是什么。其次,如何确保你使用CSS编写的界面是足够灵活的,以至于用户无法触发页面的错误也不会无法访问页面。当你理解了HTML并使用CSS来控制它的样式时,你能够减少很大的代码量。你的用户们的忠诚度依赖于所在的技术平台,如果你不打算创建友好的交互来提升用户体验,增加用户的留存度,CSS可能并不适合你。CSS被设计为一种“宽容“的语言,当你的一些代码无法起作用时,CSS也不会报错。因此,渐进增强是很棒的设计。你无需担心因添加了一行不支持的代码而出错,解析器会跳过它不支持的属性。当遇到错误时,JS解析器会中断解析并且抛出错误信息,而CSS解析器会忽略这些错误并继续解析。这对于想要知道错误信息的开发者来说会很奇怪,但是却让你从需要使用if来包含各种情况、兼容所有可能使用的浏览器这一状况下解脱出来。如何对按钮使用渐变效果?首先,定义一个背景色,然后在下一行设置背景为渐变。如果浏览器不支持渐变效果,它依旧会渲染出一个正常的按钮,只不过背景不是渐变而已。在这个过程中,你根本无须担心浏览器是否支持渐变。由于对CSS的设计目的不了解而产生了错误认知,才导致出现了很多“CSS不是真正的编程”的观点。如果你想要完全控制一切,比如界面、甚至精细到像素的话,请不要使用CSS。相反,如果你想要构建一个包罗广泛、多种多样的页面,CSS是个很好的工具。编写CSS需要站在用户的角度考虑,设计拥有良好交互的页面,提升用户体验,但这并不是说你把一个Photoshop生成的图片放到页面就好了。使用CSS构建页面需要不同于后端语言的技术栈,其次,作为维护者、编写者的心态也要发生转变。不管怎么说,轻视CSS开发者、将他们视为非纯正开发者,这种傲慢的想法略显荒谬。尤其是在你甚至都没花时间了解CSS的设计目的是什么,以及它目前惊人的发展速度。从另一方面来说,CSS本不是也不应该是任何问题的解决方式。例如,你可以创建带有阴影的像素,但同时也会对浏览器渲染引擎带来渲染压力。对我来说,CSS就是Web的一部分;对有些人来说,CSS的语法显得很奇怪,以至于让他们觉得是另一种编程语言。不过这些年来,随着CSS的发展,它的价值毋庸置疑。在未来很长一段时间,CSS应该也不会消失。因此,如果你不喜欢使用CSS,那就和会使用的人合作开发网页。与其讨论“CSS是否有缺陷,需要被替代”的问题,不如以一种积极健康且不同于以往的角度讨论CSS:CSS可以做什么,它有什么不足有哪些过去需要其他技术才能实现的,而现在CSS就可以做到的事情,以及如何应用如何编写可维护的CSS你能够做什么,来使CSS开发者的开发过程更简单、容易?我们使用哪些CSS hack,为什么不应该再用它们我们可以做什么来让CSS这门语言变得更好、更丰富?

April 19, 2019 · 1 min · jiezi

CSS3中的box-sizing(content-box与border-box)

CSS3中的box-sizing 属性允许以特定的方式来指定盒模型,有两种方式:content-box:标准盒模型,又叫做 W3C盒模型,一般在现代浏览器中使用的都是这个盒模型border-box:怪异盒模型,低版本IE浏览器中的盒模型现代浏览器和IE9+默认值是content-box。 语法格式:box-sizing:content-box | border-box区别:content-box:padding和border不被包含在定义的width和height之内。 盒子的实际宽度=设置的width+padding+borderborder-box:padding和border被包含在定义的width和height之内。 盒子的实际宽度=设置的width(padding和border不会影响实际宽度)

April 19, 2019 · 1 min · jiezi

CSS伪元素(content与counter)

前面介绍过CSS里的::before和::after这两个伪元素,以及content相关的用法,这篇将针对content搭配counter(计数器)进行一些有趣的应用,相信熟练之后搞不好很好玩也说不定。counter基本用法在CSS里头,counter是个很有意思的功能,最常见得就是如果我们使用list清单,样式选择decimal十进制,当清单变多的时候数字也会跟着增加,底层貌似就是使用counter来做的,也因为counter所产生的数值并不存在于网页的元素内,所以如果我们要在清单元素之外使用,就必须透过::before或::after的content来实现。counter最的基本用法一定要有一个父元素和子元素(类似list的原理,使用ul包着li),所以长相会类似下面这段html:<div> <span>钢铁人</span> <span>美国队长</span> <span>雷神索尔</span></div>在CSS里头,先针对div父元素使用counter-reset:num;进行计数器归零的设置,里面num是计数器累加数值的变数,接着可以在span::before里面看到counter-increment:num;这一段,这段的作用是把num累加上去,预设数值为加1,接着就透过content显示出来。计数器预设的显示语法为:counter(计数器名称,list-style-type)div{ counter-reset:num;}span{ display:block;}span::before{ counter-increment:num; content:counter(num) ‘. ‘;}透过指定一开始counter-reset的起始数值,还有counter-increment累加的间隔数值,就可以做出从某个数值开始或只显示偶数、奇数的效果。div{ counter-reset:num 3;}span{ display:block;}span::before{ counter-increment:num 2; content:counter(num) ‘. ‘;}如果要更换数字的样式,也可以透过计数器的第二个设定值list-style-type来更改,下面的例子就是将样式更改为georgian。div{ counter-reset:num;}span{ display:block;}span::before{ counter-increment:num; content:counter(num, georgian) ‘. ‘;}counter进阶用法除了指定单一个变数外,counter也可以同时指定多个变数,例如下面这段HTML,有三个类别在里面,我分别用span、i和b来分类。<div> <span>钢铁人</span> <span>美国队长</span> <span>雷神索尔</span> <i>宙斯盾局</i> <i>神鬼局</i> <i>神经局</i> <b>九头蛇</b> <b>九头牛</b> <b>九头猪</b></div>CSS一开始counter-reset可以指定多个变数,透过一个空白字元分隔,如果空白字元后面接着数字则是起始值,没有数字预设为0,当这样设定之后,就可以看到不同类别的数字代号就不同。如果遇到了巢状结构,需要一层层的展开(例如:1 > 1.1 > 1.1.1),采用上述的作法可能就会复杂许多,好在counter还提供了另外一个counters的功能,目的就是来解决巢状结构的麻烦事,在开始前可以先看看透过ul和li组合的清单长相:<ul> <li>第一层 <ul> <li>第二层 <ul> <li>第三层</li> <li>第三层</li> <li>第三层</li> </ul> </li> <li>第二层</li> <li>第二层</li> </ul> </li> <li>第一层</li> <ul> <li>第二层</li> <li>第二层</li> </ul></ul>传统的清单如果将list-style设为decimal,同样可以具备数字接续的功能,但相对来说要做一些特殊变化就办不到了。li{ list-style:decimal;}透过content和counters的搭配,我们就可以告别预设值的困扰,甚至可以在不使用清单ul和li的状况下,实现和清单一模一样的效果,举例来说,我们纯粹透过div模拟一个清单的长相(状态仍然必须是有父元素和子元素的概念),里面的样式b就等于是ul,样式a就等于是li:<div class=“a”>第一层 <div class=“b”> <div class=“a”>第二层 <div class=“b”> <div class=“a”>第三层</div> <div class=“a”>第三层</div> <div class=“a”>第三层</div> </div> </div> <div class=“a”>第二层</div> <div class=“a”>第二层</div> </div></div><div class=“a”>第一层 <div class=“b”> <div class=“a”>第二层</div> <div class=“a”>第二层</div> </div></div>由于b的外层没有东西,所以一开始要把body和b都进行counter reset的动作,接着透过counters的使用,让计数器的数值可以一个接着一个放进去,如此一来就可以做到原本清单不容易实现的效果了。counters使用语法:counters(计数器名称,分隔字,list-style-type)body, .b{ counter-reset:c;}.a::before{ content:counters(c, “.”) “:”; counter-increment:c; }div{ margin-left:10px;}了解原理之后,透过 ::before 和 ::after 的交互应用,就可以做出颇具特色的列表效果。body, .b{ counter-reset:c;}.a{ box-sizing:border-box; position:relative; line-height:40px;}.a .a{ padding-left:30px;}.a::after{ content:’’; box-sizing:border-box; display:inline-block; position:absolute; z-index:-1; top:0; left:0; width:100%; height:40px; margin-left:30px; box-shadow:inset 0 2px #666; background:#eee;}.a::before{ content:counter(c, upper-roman); counter-increment:c; display:inline-block; width:30px; height:40px; background:#666; color:#fff; text-align:center; margin-right:5px;} ...

April 18, 2019 · 1 min · jiezi

你从来没了解过的CSS浮动 | Design Shack

浮动到底是做什么呢?他们是如何影响相关元素的盒模型的呢?浮动的元素与内联元素有什么不同呢?制定浮动元素的位置的具体规则是什么?clear属性是如何工作的,并且它的作用是什么?即使是经验丰富的开发者也会在浮动上出错,所以理解浮动的行为能帮你摆脱面对CSS的很多困扰。即使你认为你已经了解了关于浮动的所有知识,我们也会深入到你可能会学习到新知识的地步。什么是浮动?在CSS中一些元素是块级元素,他们会自动启用新的一行。例如,如果你创建2个单独的单词段落元素,它们不会相互流入而会各自出现在单独的一行。另一种元素是内联元素,它们会与之前的内容保持显示在“一行”。举个例子,有个锚标签,它可以出现在像段落这个元素之内而且不会造成任何额外的空白,也不会另起一行。改变这种布局模型的一种方式是使用浮动,它允许给定的元素挪动到它那一行的一侧,并且其他内容向下流动。一个右浮动的元素将被推动直到它的容器的右侧,并且内容会沿着它的左侧向下流动,一个有浮动的元素会被挪动到左侧,内容会沿着它的右侧向下流动。有一个经典的例子是当你将一张图片放进一段落里,并且想让两者并排出现而不是堆叠。首先,我们用HTML创建两个元素:<img src=“http://lorempixum.com/200/200/" /><p>Lorem ipsum…</p>仅仅这样并不能出现我们想要的效果。段落元素是一个块级元素,它会独自占一行,所以段落和图片会显示在正常的文档流。我们可以通过将图片浮动到右边来改变显示。这种CSS非常基础:img { float: right; margin: 20px;}有了这段代码,我们的图片会被挪到它这一行的右边,段落文字会在它的左边向下流动。有趣的是,这张图片在被浮动时,我们其他的内容将会尽可能的尝试围绕它出现。如果改变我们容器的大小或者将浏览器窗口变窄,文本只是简单的自我重排而不会触碰到图片。这种盒子是怎么工作的可能你对这种行为已经有了很好的理解。然而,为了更充分利用浮动,你需要更深层次的理解这两个元素之间的交互。例如,我们怎么在段落和图片之间添加额外的空白?你可能认为这样可行p {margin: 20px;}然而,这不会在图片和段落之间产生甚至一像素额外的空白。相反,我们为图片应用了这些空白:img {margin: 20px;}你应该问自己一个问题,“为什么?”为什么增加段落的margin不会在图片和段落之间增加空白?因为我们没有掌握属于段落的盒模型。如果你曾怀疑你的布局是怎么在基本层面上工作的,试着应用一两个边框来看看将发生什么。如果我们在这个段落上这样做,结果可能会让你惊讶,p { border: solid 1px black;}正如你所看到的,图片实际上在段落的盒子里面!这解开了我们的边界空白疑惑。我们给段落添加的任何空白都被应用到了图片的右边,这就是为什么在图片和段落之间不会增加空白。如果我们想改变这种行为,让段落不再围绕图片,我们可以让段落浮动到左边,并给他指定宽度(没有表示宽度,段落将会是100%的宽,将不会布置在图片旁边)。疯狂的浮动规则现在我们知道了浮动是做什么的,以及它是怎么影响所涉及的元素的盒子的。让我们继续讨论另一块可能很多开发者都不了解的信息:控制浮动元素位置的规则。通常在图片集或者特征列表中开发人员会用浮动来控制列表项的位置。我们创建一个简单的纯图片的列表来看看这是怎么工作的。<ul> <li><img src=“http://placehold.it/100x100&text=1"/></li> <li><img src=“http://placehold.it/100x150&text=2"/></li> <li><img src=“http://placehold.it/100x100&text=3"/></li> <li><img src=“http://placehold.it/100x100&text=4"/></li> <li><img src=“http://placehold.it/100x100&text=5"/></li> <li><img src=“http://placehold.it/100x150&text=6"/></li> <li><img src=“http://placehold.it/100x100&text=7"/></li></ul>默认情况下,所有的列表项都显示在一个大的垂直栈里,这意味着它们是块级元素。即使图片是内联元素,它们也被它们的父块级元素列表项控制。为了解决这种问题,我们要使列表项左浮动。当一行中多个元素被浮动,它们会产生同内联元素的流类似的效果。然而,正如我们所看到的,有一些关键的区别。li { float: left; margin: 4px;}现在,如果我们所有的图片都是同样的高度,这只是一个非常普通的例子。这个结果看起来就像是一个简单的从左到右按顺序排列的图片集:然而,我们的图片并不是同样的高度,一些高100px,其他的高150px。这会造成非常古怪的结果。我第一次看到这种结果的时候感到很困惑。这里的世界到底发生了什么?为什么图片4会像那样在右边?难道它不应该自己试着尽可能让自己浮动到左边?如果我们去掉这些列表项的浮动,用display:inline来替代,结果是截然不同的。li { display: inline;}跟最开始相比,这个例子的不同之处在于图片的默认状态是沿着它们的底部边缘垂直对齐。这会使它们同之前的例子看起来非常不一样,但是我们可以使用一行CSS来解决这个问题:img{ vertical-align: top;}现在看起来比较像浮动示例了,只会显示有一个可预测顺序的内联列表项。当x轴没有下一项的空间时,它会返回到下一行的左侧。所以,我们的浮动图片集为什么没有像这样显示呢?为什么浮动的列表项被奇怪的巫术控制?九条规则事实证明,CSS规范列出了九条规则来规定浮动的行为。这个清单的问题在于即使是被写出来了,也只有无聊的人才能去理解它。这里是一个从其中一个规则中引用的:“If the current box is left-floating, and there are any left-floating boxes generated by elements earlier in the source document, then for each such earlier box, either the left outer edge of the current box must be to the right of the right outer edge of the earlier box, or its top must be lower than the bottom of the earlier box. Analogous rules hold for right-floating boxes.”也许你的阅读理解能力比我要强,但是这个还有其他的规则都让我头晕。所有说什么左外边缘在右外边缘的右边的言论都是非常平常的东西,却被装扮得听起来十分复杂。为了你们的方便,Josh Johnson将浮动表现翻译成英文并总结了九条规则,看起来更简单一点。浮动元素会被推到他的容器的边缘。任何浮动元素都会出现在他之前的浮动元素的旁边或是下方。如果元素都是左浮动,那么第二个元素将会出现在第一个元素的右边,如果都是右浮动,第二个元素会出现在第一个元素的左边。左浮动的盒子不能出现在右浮动盒子的右边。浮动元素不能高过他的容器的上边缘(当涉及到塌陷的边距这将会更复杂,请参考最初的规则)。浮动元素不能比前一个块级元素或浮动元素高。浮动元素不能高过前一行内联元素。靠着另一个浮动元素的浮动元素不能超出自己的父容器边缘。一个浮动的盒子必须尽可能的高的放置。一个左浮动的盒子必须尽可能左的放置,一个右浮动的盒子要尽可能的右的放置。尽可能高的位置的优先级比左右高。我们可以看做这些大多数都是常识,但他们也要被明确声明出来以便每个人在每个浏览器上看到同样的页面。基本上来说,这种情况的要点就是浮动元素会被指定到具体的边缘(左或者右),没有过多的要求。除非在他之前有另一个浮动元素,他就会与那个浮动元素相邻。真正让我们感到困惑的是最后的规则,它表明了浮动元素尽可能的保持高的位置,并且垂直定位规则比水平的左右规则在将元素推到边缘时优先级更高。在我们之前的例子中,数字2的图片把行的高度撑开了,以至于在数字3图片之后仍然有一些垂直的空间使得数字4图片挤了进去。即使是考虑到这些规则,这种模式也不容易被预测到。请记住,当你有一个浮动元素时,后面紧接着的浮动元素至少要占据与之前同样或者更多的垂直高度来打破这一行使得流动下移。浮动顺序关于我们在这里提出的规则的最后一点。第2条规则会对那些浮动元素产生有趣的影响。假设,我们有六张从一到六的数字图片,如下:<ul> <li><img src=“http://placehold.it/100x100&text=1"/></li> <li><img src=“http://placehold.it/100x100&text=2"/></li> <li><img src=“http://placehold.it/100x100&text=3"/></li> <li><img src=“http://placehold.it/100x100&text=4"/></li> <li><img src=“http://placehold.it/100x100&text=5"/></li> <li><img src=“http://placehold.it/100x100&text=6"/></li></ul>如果我们将这些图片浮动到左边,他们会按顺序出现,从一开始直到六,从左到右,从上到下。但是,如果我们将这些图片右浮动。正如你所看到的,第一张图片被放到了最右边的位置。类似的,在换行时,第四张图片也被放置到了右边。这就是为什么你很少看到有人将导航元素右浮动。弄出了这样螺旋状的顺序就需要HTML结构做出不受欢迎的改变来解决。清除浮动浮动对于完成一些类似于创造内容列这样的布局很有用。但是,一旦浮动被声明,他们就会对剩下的文档的产生一些或许你喜欢或者不喜欢的影响。例如,我们想在我们之前提及的那种左浮动的列表块之后加一个段落。结果或许并不是你所期望的:这里的结果是使用clear属性,让没有浮动的元素可以出现在他所应用的元素给定的一侧。例如,比如我们在我们的小图库列表的第二项添加clear: left.ul li:nth-child(2) { clear: left;}这段代码告诉浏览器,第二项的顶部必须位于他之前的任意左浮动项的底部之下(在这个例子中,是第一项)。如果我们将所有元素都右浮动,我们就要使用clear: right。请注意,在此之后,剩下的浮动元素都会保持他们的位置。因为他们仍然被设置的左浮动,清除属性并没有取消他们的表现。这意味着我们的问题不能通过清除列表任意项的浮动来解决。相反,必须被清除的是段落元素,他是一个没有被浮动的块级元素。这将确保他会出现在浮动元素的下面而不是旁边。p { clear: both;}从技术上来讲,我们只需要清除这里的左浮动,但是当一个开发者想要确保清除所有浮动时,通常会看到clear的值被设置为both。这个变化能非常好的解决我们的问题。浮动的怪癖和清除浮动当一个给定的元素只包含浮动元素时会产生奇怪的现象:父元素的高度会坍塌。举例说明,假设我们想在所有示例中的无序列表给定背景色。如果列表的元素没有被浮动,我们就只需要应用一下CSS来添加背景。ul { background: gray;}正如你所看到的,定义的无序列表的框已经变成了灰色,并且列表中的每一项都堆叠在一起。然而,在第二个例子中,我们浮动了所有列表项,UL只包含了浮动的元素,导致它的高度坍塌,这会让新手开发者惊讶它的背景色到底发生了啥。有一些方法可以解决这个问题,最简单容易的方法是为无序列表的父元素明确指定高度。ul { height: 300px;}正如你所看到的,这的确能让我们的背景重新被填满。然而,从长远来看这并不是一个可取的办法,如果高度是基于内容自动计算的话。如果我们要添加三个或者更多的图片到列表库里,高度就会不足。为了解决而清除浮动这里术语clear fix,也叫clearfix,就开始发挥作用啦。清除浮动通过使用clear属性来解决高度坍塌的问题。开发者在他们的HTML中创建一个跟浮动元素同级的空元素(常常是一个div),然后给空容器添加一个命名为clearfix的class。我们回到CSS中,给clearfix添加清除浮动的属性。.clearfix { clear: both;}立刻就解决了高度坍塌的问题:根据我们已经学过的可以知道为什么这个方法可以解决我们的问题。高度坍塌的原因是因为父元素只包含了浮动元素的,现在他有了一个子元素,尽管这个子元素是空的,但是它没有浮动,所以高度会再次跟预期一样自动生成。这个方法有个问题是,没有人喜欢HTML中额外的丑陋的元素。它根本不是语义的,意味着它不能清晰的传递页面的层次结构。新的解决方案是利用overflow属性,这个属性控制了超出其包含框边界的内容的功能。如果你将父项目的overflow设置为hidden或者auto,也能解决高度坍塌。ul { overflow: auto;}这绝对是解决高度坍塌问题的最简单、最优雅的方案,也应该是你的方案。虽然这么说,那在某种情况,你想将元素的overflow设置为visible,你又应该怎么做呢?这样的结果可以使用Nick Gallagher的微型清除浮动hack,他使用了一下天才般的CSS来解决这个问题。首先,他是使用的:before以及:after来添加一些内容,我们可以用来为父元素创建一些不浮动的元素。然而,你不想在这里使用真实的内容,所以我们使它为空并且设置display为table来创建一个匿名框(空的而且不占空间)最后使用我们的老朋友clear。它创建了一个不可见的块级元素来解决高度坍塌的问题并且没有添加额外的HTML。IE的老版本需要自己修复。/* For modern browsers /.cf:before,.cf:after { content:””; display:table;}.cf:after { clear:both;}/ For IE 6/7 (trigger hasLayout) */.cf { zoom:1;}结论文章中,我们讨论了大量的信息,包括基础和复杂的。我们从最开始讨论如何浮动,以及他们在基本层级是如何工作的,然后是如何设置元素的浮动来影响元素所涉及的盒子边框,让你找出怎么样工作才能根据你想的那样获得边距。接着,我们讨论了浮动元素的基本规则,并得出了一些有趣的结论,就是不同高度的浮动元素如何定位,以及右浮动的元素怎么以相反的顺序出现。如果在阅读本篇文章之前,浮动让你感觉到很困惑,那么开始阅读吧。它们最开始就迷惑了我们。希望你现在以及了解了浮动是怎么工作的,以及知道如何使用它们来实现你想要的布局。如果你觉得这些信息有帮助,请在下面留下评论让我们知道。 ...

April 18, 2019 · 1 min · jiezi

CSS 实现三角形,非 Hack

写过 HTML upvote arrow(向上箭头),speech bubble(对话气泡)或其他类似的尖角元素的人都知道,为了创建一个纯 CSS 实现的三角形,必须使用某些 Hack。最流行的两种方式是通过 边框实现,或 Unicode 字符。不得不说,这些 CSS Hack 都非常聪明,但它们却算不上好的解决方案,代码不优雅且不够灵活。例如,我们无法在三角形上使用背景图片,因为边框和字符只能使用颜色。译注: speech bubble(对话气泡)如下图:使用 Clip-pathClip-path 是 CSS 规范中新属性中的一个,它能让我们只显示元素的一部分并隐藏其余部分。其工作原理如下:假设我们有一个普通的矩形 div 元素。你可以在下面的编辑器中单击 Run 运行并查看渲染后的 HTML。(译注:原文内有在线代码编辑器,此处仅贴出代码,可自行 copy 测试。)div { width: 200px; height: 200px; background: url(https://goo.gl/BeSyyD);}<div></div>为了实现三角形,我们需要使用 polygon() 函数。其参数为以逗号分隔的平面坐标点,这些坐标点定义了我们的剪切遮罩的形状。三角形 = 3个点。可以试着更改值并查看形状是如何变化的。div { width: 200px; height: 200px; background: url(https://goo.gl/BeSyyD); /* 三个点分别为:中上的点,左下的点,右下的点 */ clip-path: polygon(50% 0, 0 100%, 100% 100%);}<div></div>创建的路径中的所有内容都会保留,而路径外内容会被隐藏。通过这种方式,我们不仅可以制作三角形,还可以制作出各种不规则的形状,且这些形状可像普通的 CSS 块一样。(译注:即可以正常运用 CSS 属性在这些形状上)这种方法唯一的缺点就是是我们需要自行计算点的坐标来得到一个好看的三角形。不过,它比使用边框或▲(译注:正三角的 Unicode 字符)更好。浏览器支持如果你查看 clip-path 的 caniuse 页面,一开始你发现貌似兼容性非常不好,但事实上,该属性在 Chrome 中能正常工作,且不需要前缀,在 Safari 中需要加 -webkit- 前缀。Firefox 53 版本以上可用。IE / Edge 一贯的不支持,未来也许会支持。关于 clip-path 属性有很多小技巧,包括 SVG 的“奇幻”用法。了解更多,请查看下面的链接。MDN 上的 clip-path - 链接Codrops 上的深入 clip-path 教程 - 链接Clippy, 一个 clip-path 生成器 - 链接 ...

April 18, 2019 · 1 min · jiezi

Flexbox 很棒,但有些情况不适用

对大部分的人来说(如果你写过CSS),Flexbox 可以说是完美,但它是否适合所有场景呢?简而言之,我会给出几种可用的场景,需要你重新思考 Flexbox 模型的使用。顺便说一句,本人是 Flexbox 的忠实粉丝,曾写过一篇 Flexbox 详解 ,可以算得上最全面的文章了。当然,我们应该了解该如何使用 Flexbox,哪些情况不应该使用,以及用在哪些地方最好。以下是我选出的最不应该是使用 Flexbox 的3种场景,并附带原因。1. 把Flexbox当作一个网格系统使用长久以来,很多人(包括我自己)都在滥用 CSS 盒模型。从复选框点击 hack 到“纯 CSS 图形”,我们似乎沉浸在各种奇淫巧技中 —— 各种让人觉得高大上或精通的技巧。我并不反对这样做,但其实我们曲解了 CSS 规范,不是吗?我们并没有将它们用在应该用的地方 —— 主要原因之一是可以做,之二是不得不做。有时候仅仅是出于兴趣(我觉得我属于这种)。也就是说,如果你选择在布局中将 Flexbox 模型当作网格系统使用,那么就算曲解了规范。还能不能开心的玩耍了?你当然可以用,但就像滥用 CSS 盒模型。其设计初衷并不是完备的网格系统。尽管你可以任意使用 Flexbox 模型,当然本人也曾瞎用,但其初衷不改。网格布局 —— 在 2017 年火了,因为所有主流浏览器都开始支持。可以在布局中将 Flexbox 作为唯一的网格系统使用吗?额!当然不可以。为什么?如果仅仅是为了一个怎么复杂的布局,或简单的为了移动端而重构布局,确实有这种可能。其实你可以摆脱这种想法的,虽然曾经我试过只用 Flexbox 就可以完成复杂的布局 —— 仅仅是因为可以做,并探索 Flexbox 的可能性。有什么注意事项吗?有一点你一定要记住。如果你不得不兼容老版本的 IE 浏览器(它们还能支持些什么好功能么?),那会有一个大问题,因为用户什么都看不到 —— 任何东西都不会显示。但是,如果你在这些浏览器上使用 Flexbox 模型作为渐进增强,你的备用方案可能是表格布局,那么老版本 IE 用户能正常使用。Flexbox 也支持一些真正的 网格系统 才有的标准特性 —— 那些特性真的很棒。尽管它还不太“标准”。此处的标准指的是就像圣杯布局那样的常用。所以禁止将其作为一个完整的网格系统使用。后面会继续讨论。2. 完全控制其视觉位置能力越大责任越大 …然后滥用! (我加的).网格布局最棒的特点之一就是无需考虑 html 源码的顺序而可以自由的指定内容的显示位置。难道 Flexbox 模型没有顺序属性么?其实是有的。但猫有四条腿,而人类只有两条。PS:此处的意思是,毕竟还是有区别,不能同等对待不同的事物,即使外观类似.上面的猫星人很帅,但即使套上了西装,也还不是人类!毫无疑问,只有人才能算人类。这就跟通过顺序属性实现“排序”的 Flexbox 一样。仅适用于简单的重排场景,如下:flexbox重排之前flexbox重排之后但是,它仍是基于元素的 html 源码顺序的。所以,其实还是没有脱离“猫人”的本质。其对源码顺序的处理与 CSS 网格布局完全不同。网格布局是另一个话题,这里不会详细讨论。3. 多列布局我觉得应该不会有很多人会将 Flexbox 模型用来干这个,值得一提的是,除了Flexbox之外,CSS3 还提供其他增强布局方式 —— Flexbox 只是恰巧也能用来完成类似的功能。如果你想这样做,那么请考虑优先使用 CSS3 已提供的合适的布局方式。i. 排除特定形状如果你需要构建复杂的布局,并想要让内容按自定义区域排版或要特定几何形状来包裹内容,请使用正确的方法。虽然你可以将其包裹在 flex-item(弹性项)中,但还是要使用正确的方法处理排除和内容包裹。ii. 实现多列多列布局是 Indesign 等传统桌面排版软件的核心功能,当某列调整大小或不能包含所有内容时,列内的文本会自动流入另一列。如果你需要在你项目中实现这个功能,CSS3 多列布局方式可以直接实现。所以,啰嗦一句,请使用正确的方式来实现此功能。可以让多列布局的某列也同时具有 flex-item(弹性项)特性么?我没有玩过 —— 不确定。总而言之,Flexbox 还是很棒的!真的很有用。非常有必要深入了解它,了解他们应该在何时何处使用。对于 Flexbox,其最大的优势是可在整体页面中为独立部分的自由布局。觉得不错? ...

April 17, 2019 · 1 min · jiezi

Vertical-Align,你应该知道的一切

好,我们聊聊vertical-align。这个属性主要目的用于将相邻的文本与元素对齐。而实际上,verticle-algin可以在不同上下文中灵活地对齐元素,以及进行细粒度的控制,不必知道元素的大小。元素仍然在文档流中,因而其他元素可以根据它们大小的变化进行相应的调整。一个有用的例子就是居中图标与旁边的文本。Vertical-Align是个怪物可是,vertical-align有时候也很难搞,经常导致困惑。好像有什么神秘的规则在起作用。比如,我们改变了某个元素的vertical-align而它的对齐方式却并未改变,反倒是同一行的其他元素的对齐方式变了!我时不时地仍然会掉到vertical-align的坑里,绝望无助。遗憾的是,关于这方面的资料大都讲得很肤浅。尤其是针对布局的情况。很多文章概念错乱,试图把元素中的一切都垂直对齐。它们介绍了这个属性的基本概念,解释了简单情况下元素的对齐,却没涉及真正棘手的部分。因此,我给自己设定了一个目标:彻底摸清vertical-align的行为。然后我就死啃W3C的CSS规范,同时也尝试了一些例子。最终写出了这篇文章。好,下面我们就开始吧。对哪些元素可以使用Vertical-Alignvertical-align用于对齐行内元素。所谓行内元素,即display属性值为下列之一的元素:inlineinline-blockinline-table (本文未涉及)其中,行内元素(inline element)就是包含文本的标签。而行内块元素(inline-block element),顾名思义,就是位于行内的块元素。可以有宽度和高度(可以由其内容决定),也可以有内边距、边框和外边距。行内级元素会相互挨着排成行。如果一行排不下,就会在下方再建一行。所有行都会创建所谓的行盒子,行盒子装着自己行中的所有内容。内容的高度不同,行盒子的高度也不同。在下面的示意图中,行盒子的顶部和底部用红色点线表示。这些行盒子限定了我们可以影响的范围。在行盒子内部,可以通过vertical-align来对齐个别元素。那么,相对于什么来对齐元素呢?基线与外边界垂直对齐最重要的参考点,就是相关元素基线。某些情况下,行盒子的上下外边界也会成为参考点。下面我们就来看一看相关元素类型的基线和外边界。行内元素这里有三行文本紧挨着。红线表示行高的顶边和底边,绿线表示字体高度,蓝线表示基线。左边这一行,行高与字体高度相同,因此上下方的红色和绿线重叠在了一起。中间一行,行高是font-size的两倍。右边一行,行高为font-size的一半。行内元素的外边界与自己行高的上、下边对齐。行高比font-size小不小并不重要。因此上图中红线同时也就表示外边界。行内元素的基线是字符恰好位于其上的那条线,也就是图中的蓝线。大致来说,基线总是穿过字体高度一半以下的某一点。可以参考W3C规范中详细的定义。行内块元素从左到右:包含流内内容(“c”)的行内块、包含流内内容且设置了溢出(overflow: hidden)的行内块和未包含流内内容(但内容区有高度)的行内块。红线表示外边距的边界,黄色是边框,绿色的内边距,蓝色是内容区,蓝线是每个行内块元素的基线。行内块元素的外边界即其外边距盒子的上、下两边,也就是图中的红线。行内块元素的基线取决于元素是否包含流内内容:有流内内容的行内块元素,基线就是正常流中最后内容元素的基线(左)。这个最后元素的基线是按照它自己的规则找到的。有流内内容但overflow属性值不是visible的行内块元素,基线就是外边距盒子的底边(中)。也就是与行内块元素的下外边界重合。没有流内内容的行内块元素,基线同样是外边距盒子的底边(右)。行盒子这张图前面出现过。但这次我们画出了行盒子的文本盒子的上、下边(绿色,下面详细介绍)还有基线(蓝色)。同时,还用灰色背景表示了文本元素的区域。行盒子的顶边与该行中最顶部元素的顶边重合,底边与该行中最底部元素的底边重合。因此图中的红线表示的就是行盒子。行盒子的基线是个变量:CSS 2.1没有定义行盒子的基线。在使用vertical-align时这一块应该是最令人困惑的了。也就是说,基线画在哪里需要满足很多条件,比如要符合vertical-align指定的条件,同时还要保证行盒子高度最小。这是个自由变量。因为行盒子的基线并不可见,所以有时候不容易确定它的位置。但实际上有个简单的办法可以令其可见。只要在相关行的开头加上一个字母,比如上图中开头的“x”即可。如果这个字母没有被设置对齐,那么它默认就位于基线之上。围绕基线的是行盒子中的文本盒子。可以简单地把文本盒子看成行盒子内部未经对齐的行内元素。文本盒子的高度等于其父元素的font-size。因此,文本盒子只是用来盛放未经格式化的文本的。上图中的绿线表示的就是文本盒子。由于文本盒子与基线关联,所以基线移动它也会跟着移动。(W3C规范里称这个文本盒子为strut。)终于把最难的部分讲完了。现在,我们已经知道了对齐相关的一切要素。下面简单总结一下最重要的两点。有一个区域叫行盒子。行盒子中的内容可以垂直对齐。行盒子有基线、文本盒子,还有上边和下边。还有行内元素,也就是可以被对齐的对象。行内元素有基线,以及上边和下边。Vertical-Align的值使用vertical-align,前面提到的参考点就会按照某种关系被设置好。对齐行内元素的基线和行盒子的基线baseline:元素基线与行盒子基线重合。sub:元素基线移动至行盒子基线下方。super:元素基线移动至行盒子基线上方。<百分比值>:元素基线相对于行盒子基线向上或向下移动,移动距离等于line-height的百分比。<长度值>:元素基线相对于行盒子基线向上或向下移动指定的距离。相对于行盒子的基线对齐元素的外边界middle:元素上、下边的中点与行盒子基线加上x-height的一半对齐。相对于行盒子的文本盒子对齐元素的外边界还有两种情况是相对于行盒子的基线对齐,因为文本盒子的位置由行盒子的基线决定。text-top:元素的顶边与行盒子的文本盒子的顶边对齐。text-bottom:元素的底边与行盒子的文本盒子的底边对齐。相对于行盒子的外边界对齐元素的外边界top:元素的顶边与行盒子的顶边对齐。bottom:元素的底边与行盒子的底边对齐。为何Vertical-Align的行为如此乖张下面我们看看在某些情况下的垂直对齐。特别是一些容易出错的情况。居中图标有一个问题一直困扰着我。有一个图标,我想让它与一行文本垂直居中。如果只应用vertical-align: middle好像不行,比如这个例子:<span class=“icon middle”></span>Centered?<span class=“icon middle”></span><span class=“middle”>Centered!</span><style type=“text/css”> .icon { display: inline-block; /* size, color, etc. / } .middle { vertical-align: middle; }</style>还是同一个例子,只不过这次多了一些辅助线:这次可以看清问题所在了。因为左侧的情况是文本没对齐,而是仍然位于基线之上。应用vertical-align: middle,实际上会导致图标中心与不出头小写字母的中心(x-height的一半)对齐,所以出头的字母会在上方突出出来。右侧,仍然是对齐整个字体区的垂直中点。结果文本基线稍稍向下移动了一点,于是就实现了文本与图标完美对齐。行盒子基线的移动这是使用vertical-align时一个常见的坑:行盒子基线的位置会受到其中所有元素的影响。假设一个元素采用的对齐方式会导致行盒子移动。由于大多数垂直对齐(除top和bottom外),都相对于基线计算,因此这会导致该行所有其他元素重新调整位置。下面是几个例子。如果行内有一个很高的元素,这个元素上方和下方都没有空间了,此时要与行盒子的基线对齐,就必须让它移动。矮盒子是vertical-align:baseline。左侧的高盒子是vertical-align: text-bottom,而右侧的高盒子是vertical-algin:text-top。可以看到,基线带着矮盒子移动到了上方。<!– left mark-up –> <span class=“tall-box text-bottom”></span> <span class=“short-box”></span> <!– right mark-up –> <span class=“tall-box text-top”></span> <span class=“short-box”></span> <style type=“text/css”> .tall-box, .short-box { display: inline-block; / size, color, etc. / } .text-bottom { vertical-align: text-bottom; } .text-top { vertical-align: text-top; } </style>在通过vertical-align的其他值对齐一个较高的元素时,也会出现同样的现象。即使设置vertical-align为bottom(左)和top(右),也会导致基线移动。这就很奇怪了,因为此时根本不关基线什么事。<!– left mark-up –><span class=“tall-box bottom”></span><span class=“short-box”></span><!– right mark-up –><span class=“tall-box top”></span><span class=“short-box”></span><style type=“text/css”> .tall-box, .short-box { display: inline-block; / size, color, etc. / } .bottom { vertical-align: bottom; } .top { vertical-align: top; }</style>把两个较大的元素放在一行并垂直对齐它们,基线也会移动以匹配两种对齐。然后,行的高度会调整(左)。再增加一个元素,但该元素对齐方式决定了它不会超出行盒子的边界,所以行盒子不会调整(中)。如果新增的元素会超出行盒子的边界,那么行盒子的高度和基线就会再次调整。在这例子中,前两个盒子向下移动了(右)。<!– left mark-up –><span class=“tall-box text-bottom”></span><span class=“tall-box text-top”></span><!– middle mark-up –><span class=“tall-box text-bottom”></span><span class=“tall-box text-top”></span><span class=“tall-box middle”></span><!– right mark-up –><span class=“tall-box text-bottom”></span><span class=“tall-box text-top”></span><span class=“tall-box text-100up”></span><style type=“text/css”> .tall-box { display: inline-block; / size, color, etc. / } .middle { vertical-align: middle; } .text-top { vertical-align: text-top; } .text-bottom { vertical-align: text-bottom; } .text-100up { vertical-align: 100%; }</style>行内元素下面可能会有一个小间隙看看这个例子:对列表元素的li应用vertical-align。<ul> <li class=“box”></li> <li class=“box”></li> <li class=“box”></li></ul><style type=“text/css”> .box { display: inline-block; / size, color, etc. / }</style>我们看到,列表项位于基线上。基线下面有一个小间隙,用于文本的下伸部分。怎么办?只要把基线向上移开一点就行了,比如用vertical-align: middle:<ul> <li class=“box middle”></li> <li class=“box middle”></li> <li class=“box middle”></li></ul><style type=“text/css”> .box { display: inline-block; / size, color, etc. */ } .middle { vertical-align: middle; }</style>有文本内容的行内块不会出现这种情况,因为内容已经把基线向上移了。行内元素间的间隙会破坏布局这主要是行内元素本身的问题。由于vertical-align必然会遇到行内元素,所以有必要了解一下。在前面列表项的例子中也可以看到这个间隙。这个间隙来自你的标记中行内元素间的空白。行内元素间的所有空白会折叠为一个。如果我们要通过width: 50%实现并排放两个行内元素,那这个空白就会成为障碍。因为一行放不下两个50%再加一个空白,结果就会折行(左)。要删除这个间隙,需要在HTML中通过注释删除空白(右)。<!– left mark-up –><div class=“half”>50% wide</div><div class=“half”>50% wide… and in next line</div><!– right mark-up –> <div class=“half”>50% wide</div><!—-><div class=“half”>50% wide</div><style type=“text/css”> .half { display: inline-block; width: 50%; }</style>Vertical-Align揭秘好了,就这些。一旦了解了规则,就没有那么复杂了。如果vertical-align没有达到效果,只要问下面的问题就能找到症结所在:行盒子的基线和上、下边界在哪儿?行内元素的基线和上、下边界在哪儿?据此就可以找到问题的解决方案。 ...

April 17, 2019 · 2 min · jiezi

一次搞懂CSS字体单位:px、em、rem和%

对于绘图和印刷而言,“单位”相当重要,然而在网页排版里,单位也是同样具有重要性,在CSS3普及以来,更支持了一些方便好用的单位(px、em、rem…等),这篇文章将整理这些常用的CSS单位,也帮助自己未来在使用上能更加得心应手。“网页”和“印刷”的单位若要把单位做区隔,最简单可以分为“网页”和“印刷”两大类,通常对于CSS来说只会应用到网页的样式,毕竟真正要做印刷,还是会倾向透过排版软件来进行设计。网页(单位)px:绝对单位,代表屏幕中每个「点」(pixel)。em:相对单位,每个子元素透过「倍数」乘以父元素的px值。rem:相对单位,每个元素透过「倍数」乘以根元素的px值。%:相对单位,每个子元素透过「百分比」乘以父元素的px值。网页(属性名称)medium:预设值,等于16px(h4预设值)xx-small:medium的0.6倍(h6预设值)x-small:medium的0.75倍small:medium的0.8倍(h5预设值,W3C定义为0.89,实测约为0.8)large:medium的1.1倍(h3预设值,W3C定义为1.2,实测约为1.1)x-large:medium的1.5倍(h2预设值)xx-large:medium的2倍(h1预设值)smaller:约为父层的80%larger:约为父层的120%印刷pt:打印机的每个「点」,定义为1 pt=1/72 in,如果在72 dpi的系统上1 px = 1 pt,但如果在96dpi的系统上1 px = 0.75 pt(72/96 = 0.75)。in:英寸,在96 dpi的系统上1 in = 96 px。cm:厘米,在96 dpi的系统上1 cm = 37.795275593333 px。mm:毫米,在96 dpi的系统上1 cm = 3.7795275593333 px。示例展示以下将展示四种不同单位的示例,为公平起见,四个示例都套用预设的div格式,纯粹改变font-size看看有何不同,由于子元素若没有设定font-size,会自动继承父元素的font-size,使用上就应该要预先初始化字体大小,下面这两段CSS可以将所有的元素字体大小预设为16px,然后可个别进行调整。html{ font-size:16px;}html * { font-size: 1rem;}1. pxpx是绝对单位,因此只要设定多少px,就会精确的呈现,对于一些讲求精准位置的排版而言十分有用,如示例表示的,指定多大px字体就会多大。<div style=“font-size:16px;">16px <div style=“font-size:20px;">20px <div style=“font-size:24px;">24px <div style=“font-size:16px;">16px <div style=“font-size:32px;">32px</div> </div> </div> </div></div>2. emem是相对单位,为每个子元素透过“倍数”乘以父元素的px值,如果我们每一层div都使用1.2em,最内层就会是16px x 1.2 x 1.2 x 1.2 x 1.2 x 1.2 = 39.8px。(浏览器预设字体大小为16px,若无特别指定则会直接继承父元素字体大小)<div style=“font-size:1.2em;">1.2em <div style=“font-size:1.2em;">1.2em <div style=“font-size:1.2em;">1.2em <div style=“font-size:1.2em;">1.2em <div style=“font-size:1.2em;">1.2em</div> </div> </div> </div></div>3. remrem是相对单位,为每个元素透过“倍数”乘以根元素的px值,如果我们每一层div都使用1.2rem,最内层就会是16px x 1.2 = 19.2px。(根元素指的是html的font-size,预设为16px)<div style=“font-size:1.2rem;">1.2rem <div style=“font-size:1.2rem;">1.2rem <div style=“font-size:1.2rem;">1.2rem <div style=“font-size:1.2rem;">1.2rem <div style=“font-size:1.2rem;">1.2rem</div> </div> </div> </div></div>4. %%百分比是相对单位,和em大同小异,简单来说em就是百分比除以一百,如果我们每一层div都使用120%,就等同于1.2em,最内层就会是16px x 1.2 x 1.2 x 1.2 x 1.2 x 1.2 = 39.8px。<div style=“font-size:120%;">120% <div style=“font-size:120%;">120% <div style=“font-size:120%;">120% <div style=“font-size:120%;">120% <div style=“font-size:120%;">120%</div> </div> </div> </div></div>5.small、medium、large…等字体大小的属性有七种,分别是xx-small、x-small、small、medium、large、x-large和xx-large,除了x-small,其余六种分别对应h6~h1的标签文字大小,根据W3C的规范,以medium预设16px为基础(若html字体预设大小改变,medium也会跟着变),使用固定的百分比乘以medium的大小,例如ss-small预设为16px x 0.6 = 9.6px(浏览器显示为12px)。<div style=“font-size:xx-small;">xx-small <div style=“font-size:x-small;">x-small <div style=“font-size:small;">small <div style=“font-size:medium;">medium <div style=“font-size:large;">large <div style=“font-size:x-large;">x-large <div style=“font-size:xx-large;">xx-large</div> </div> </div> </div> </div> </div></div>6. larger、smallerlarger和smaller就是固定百分比为单位,larger为父层的120%,smaller为父层的80%。<div style=“font-size:medium;">medium <div style=“font-size:larger;">larger <div style=“font-size:larger;">larger <div style=“font-size:larger;">larger <div style=“font-size:smaller;">smaller <div style=“font-size:smaller;">smaller <div style=“font-size:smaller;">smaller</div> </div> </div> </div> </div> </div></div>小结熟悉了字体大小单位之后,就更能有系统的设计整个网站的CSS构架,不过font-size本身和font-family有着一些复杂的关系,不同的font-family有时也会影响font-size的设定,因此使用上还是得稍微注意一下啰! ...

April 17, 2019 · 1 min · jiezi

css元素(before与after)

最近因为一些网页的需要,比较深入的使用了CSS的“伪元素”(Pseudo Element),发现原来不只是用用before或after而已,可以玩的东西还真是不少,所以就来篇文章,把这些比较不常玩的用法纪录一下。什么是“伪元素”?“伪元素”之所以称作“伪”,除了英文从“Pseudo”翻译过来之外,就是因为它并不是真正网页里的元素,但行为与表现又和真正网页元素一样,也可以对其使用CSS操控。跟伪元素类似的还有“伪类”(Pseudo classes),在W3C的定义里总共有五个伪元素(其他仍在测试阶段),分别是::before、::after、::first-line、::first-letter和::selection,为了和伪类区分,伪元素使用两个冒号“::”开头,而伪类使用一个冒号“:”开头(像是:hover、:target…等)。虽然现在的浏览器就算写一个冒号也可以正常运作,不过为了方便区分,用两个冒号还是比较好的,而且不论浏览器是什么,::selection必须是两个冒号才能正常运作。认识::before与::after::before、::after大概是最常使用的伪元素,两者都是以display:inline-block的属性存在,::before是在原本的元素“之前”加入内容,::after则是在原本的元素“之后”加入内容,同时伪元素也会“继承”原本元素的属性,如果原本文字是黑色,伪元素的文字也会是黑色。举例来说,下面这段代码,有一个div内容是“大家好,我是div”,使用::before、::after之后,会在原本div的前后各添加一段文字,并且让这两段文字都呈现红色。div::before{ content:“我是 before”; color:red;}div::after{ content:“我是 after”; color:red;}实用的content上述的内容乍看之下很容易理解,比较需要注意的是一定要具备content的属性,就算是只有content:“”;都可以,因为没有content的伪元素是不会出现在画面上的,然而content是个很特别的属性,它可以使用attr直接获取内容元素的属性值(attribute),举例来说,在HTML里有一个超链接,点击后会弹出新视窗并连接至Google:<a href=“https://www.google.com” target="_blank">google</a>使用下列的代码用法,将会把超链接的href内容与target内容,透过伪元素一前一后的显示出来。a::before{ content: attr(href); color:red;}a::after{ content: attr(target); color:green;}此外content内容是可以“相加”的,不过用法不像JavaScript使用+号来相连,而是直接用一个空格键就可以不断的累加下去,以下面的代码来说,可以在刚刚撷取的超链接文字后方和target属性前方,加入标点符号。a::before{ content: “( " attr(href) " ) < “; color:red;}a::after{ content: " > ( " attr(target) " ) “; color:green;}content甚至可以使用url放入图片图片的功能,下列的代码会呈现出三张图片。div::before{ content:url(图片网址) url(图片网址) url(图片网址);}content搭配quotes使用在CSS里有个不常用的属性就是quotes,这是做为定义“括号格式”的属性,也就是如果在一段文字被<q></q>包住,这段文字的前后就会出现自定义的括号,而且quotes支持巢状的结构,也就是你可以一层层的写下去,以下面这段HTML文字举例:最外层<q>第一层<q>第二层</q><q>第二层<q>第三层</q></q></q>quotes的属性如果只写一层,就会看到只出现一种括号,前后括号使用空白区隔,两组为一个单位,前后可以不同符号。q{ quotes: ’ < ’ ’ > ‘;}如果写了三层,就会看到出现三种括号,支持把文字当作括号使用。q{ quotes: ’ < ’ ’ > ’ ’ ya ’ ’ ya ’ ’ ( ’ ’ ) ’ ;}同样的道理,我们可以应用在content里面,而且透过伪元素已::before和::after已经处于前后的预设位置,甚至不用<q></q>就实现前后括号的效果,以下面这段HTML文字举例,把刚刚的q全部换成span:最外层<span>第一层<span>第二层</span><span>第二层<span>第三层</span></span></span>CSS的部分比较特别,在伪元素content里使用了open-quote(启始括号)和close-quote(结束括号)这两个有趣的值,换句话说open-quote对应到<q>,close-quote对应到</q>,此外也由于括号是在伪元素内,就可以指定不同的颜色或样式了。span{ quotes: ’ < ’ ’ > ’ ’ ya ’ ’ ya ’ ’ ( ’ ’ ) ’ ;}span::before{ content:open-quote; color:red;}span::after{ content:close-quote; color:#aaa;}小结虽然说伪元素很好用,但伪元素的内容实际上不存在网页里(如果打开浏览器的开发者工具,是看不到内容的),所以如果在里头塞了太多的重要的内容,反而会影响到SEO的效果,因此对于使用伪元素的定位,还是当作“辅助”性质会比较恰当。 ...

April 17, 2019 · 1 min · jiezi

css3元素出现动画实例

css3中实现动画一般有两种方式,一个是transition过渡,一个是animation动画。最主要区别就是transition需要条件触发,通常会用hover来触发,而animation则更灵活,可以自动播放,也可以通过条件触发。那么,如何实现一个元素出现动画呢?如果是transition,可以很轻松的实现这一效果,例如.box{ visibility:hidden; opacity:0; transform:translateY(100px); transition:.3s;}.show{ visibility:visible; opacity:1; transform:translateY(0);}这样就实现了一个“从下至上,透明度从0至1”的出现动画,很常用不是吗。https://codepen.io/xboxyan/pe…当然,我们也可以用animation来实现,.box{ visibility:hidden; opacity:0; transform:translateY(100px); transition:.3s;}.show{ animation:show .5s forwards;}.hide{ visibility:visible; opacity: 1; transform: translateY(0); animation:hide .5s forwards;}@keyframes show{ to { visibility:visible; opacity: 1; transform: translateY(0) }}@keyframes hide{ to { visibility:hidden; opacity: 0; transform: translateY(100px) }}我的天,居然要写这么多,才能实现和上面一样的效果,没办法,出现和消失是两组不同的动画,所以需要定义两个动画。https://codepen.io/xboxyan/pe…元素出现动画上面简单的介绍了动画的两种实现方法。严格来讲,transition只是过渡,只是切换样式过程中有动画的效果,而animation才是真正做动画的。当然也需要根据自己的实际需求来选择。下面来看这样一个需求:通常页面上要全局显示一个消息提示,类似于toast效果。这是我通常的做法function showMessage(txt){ this.timer && clearTimeout(this.timer); var oDiv = document.getElementById(‘messageInfo’); if(!oDiv){ oDiv = document.createElement(‘div’); oDiv.className = ‘messageInfo’; oDiv.id = ‘messageInfo’; document.body.appendChild(oDiv); } oDiv.innerHTML = ‘<span>’+txt+’</span>’; oDiv.classList.add(‘show’); this.timer = setTimeout(function(){ oDiv.classList.remove(‘show’); },2000)}原理就是,向页面添加一个div#messageInfo容器,然后添加类名.show让元素出现,2s后自动移除.show实现隐藏,效果如下https://codepen.io/xboxyan/pe…可以很明显的看到一个效果就是,第一次出现的时候是没有动画的,以后就正常了。可能平时项目中,这一点小瑕疵也没什么影响,毕竟很大一部分人连动画都不给啊,直接就是display:none和display:block,可以说是提不上体验了。那么,为什么会出现这种现象呢?首先明白一点,transition是不会自动触发的,上面是通过添加和移除类名来实现过渡效果的。但是在第一次元素刚刚创建的时候,此时在页面上该元素还未加载完成,这个时候立即添加类名,其实是可以等同于是一起创建的,没有形成过渡效果。解决这个问题很简单,就是稍微延时一下//…setTimeout(function(){ oDiv.classList.add(‘show’);},50)//…这样就基本上解决了这个问题,如下https://codepen.io/xboxyan/pe…但是,理论上这里的延迟越小越好,我测试了一下,大概和浏览器的性能有关吧,用定时器的目的也仅仅是等待元素加载完成,而dom也没有原生监听加载完成事件,所以只能用定时器估一个大概的值。但是,这仍然是一个瑕疵,从代码结构上来讲,这也是无法忍受的。那么,还有没有更好的方法呢?答案就是animationAnimation虽然从开头的例子来看,animation的写法又臭又长,但是也正体现出它的功能强大,其中之一就是自动播放动画那么,把上面的toast改造一下function showMessage(txt){ this.timer && clearTimeout(this.timer); var oDiv = document.getElementById(‘messageInfo’); if(!oDiv){ oDiv = document.createElement(‘div’); oDiv.className = ‘messageInfo’; oDiv.id = ‘messageInfo’; document.body.appendChild(oDiv); } oDiv.innerHTML = ‘<span>’+txt+’</span>’; oDiv.classList.remove(‘hide’);//默认是显示 this.timer = setTimeout(function(){ oDiv.classList.add(‘hide’);//2s后隐藏 },2000)}效果如下https://codepen.io/xboxyan/pe…其他应用场景元素添加动画通常在添加表单或者上传图片时,如果需要让新添加的元素产生一个动画效果,那么可以用到animationhttps://codepen.io/xboxyan/pe…当然,还可以做到分页加载动画,需要给每个元素添加一个延时animation-delay即可/animation-delay/.list li:not(.hide):nth-child(5n + 1) { animation-delay: .3s;}.list li:not(.hide):nth-child(5n + 2) { animation-delay: .6s;}.list li:not(.hide):nth-child(5n + 3) { animation-delay: .9s;}.list li:not(.hide):nth-child(5n + 4) { animation-delay: 1.2s;}.list li:not(.hide):nth-child(5n + 5) { animation-delay: 1.5s;}效果如下,元素会依次登场,预览窗口比较小,建议在原链接查看https://codepen.io/xboxyan/pe…然后,还可以实现九宫格动画,让元素在出现的时候从左上方依次向右下方扩散,同样是用到了animation-delayhttps://codepen.io/xboxyan/pe…很酷炫不是吗,无需用到js,也无需用到其它框架,纯天然,支持的浏览器体验更上一层楼,不支持的浏览器也无伤大雅小节总体来说,animation远比transition要强大的多,当然在实际使用中,如果有交互,如鼠标移入,首先看transition能否实现,其次才是animation,如果像这一类元素出现(生成)动画,那么就需要使用到animation了。 ...

April 16, 2019 · 1 min · jiezi

HTML5、js、CSS3的自定义表情编辑器

跟大家分享一个款基于HTML5、js、CSS3的自定义表情编辑器您可以在编辑器中为该人脸表情添加和改变各种面部表情、给它戴上帽子,戴上围巾,甚至可以将其他任意Emoji表情添加上去,是不是很酷。源码我已经分享出来了,希望对大家学习HTML5、js、CSS3有帮助。点我预览 点我下载本文作者:IT放大镜欢迎扫描二维码关注公众号,聚焦于IT,每天推送专业IT知识及行业资讯

April 12, 2019 · 1 min · jiezi

css实现水平垂直居中

纯CSS实现水平垂直居中最近的几场面试都问了这个问题,自己也只答了2种,感觉面试官不满意,特地总结了几种在开发中比较实用的方法,与大家分享。一、需要知道width,height的方案1.绝对定位 + 负外边距<style type=“text/css”> .warp{ position: relative; } .box { width: 200px; height: 100px; position: absolute; top: 50%; left: 50%; padding: 0; margin-left: -100px; /* (width + padding)/2 / margin-top: -50px; / (height + padding)/2 */ }</style><div class=“warp”> <div class=“box”> </div></div>因为margin/padding的百分比值是参照父元素的width,所以无法用百分比。二、不需要知道width,height方案1.绝对定位 + margin:auto<style type=“text/css”> .warp{ position: relative; } .box { width: 200px; height: 100px; position: absolute; padding: 0; margin: auto; left: 0; right: 0; top: 0; bottom: 0; }</style><div class=“warp”> <div class=“box”> </div></div>利用margin:auto自动计算实现2.绝对定位 + transform<style type=“text/css”> .warp{ position: relative; } .box { width: 200px; height: 100px; position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%); transform: translate(-50%, -50%); }</style><div class=“warp”> <div class=“box”> </div></div>利用translate的百分比是相对本元素宽/高这一特点实现3.flex<style type=“text/css”> .warp{ display: -webkit-flex; display: -moz-box; display: -ms-flexbox; display: -webkit-box; display: flex; -webkit-box-align: center; -moz-box-align: center; -ms-flex-align: center; -webkit-align-items: center; align-items: center; -webkit-box-pack: center; -moz-box-pack: center; -ms-flex-pack: center; -webkit-justify-content: center; justify-content: center; } .box { width: 200px; height: 100px; }</style><div class=“warp”> <div class=“box”> </div></div>其他除此之外还有table-cell布局,inline-block布局等,由于在项目中很少用到,而且感觉table布局坑太多,就不做描述,有兴趣请自行查找。本文如有错误,请在评论区提出。最后,四月求个offer~~谢谢 ...

April 11, 2019 · 1 min · jiezi

5个惊艳的CSS3、js动画库

Animates动画库magic.css动画库Bounce.js动画库Effect.css动画库hover.css动画库

April 10, 2019 · 1 min · jiezi

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

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

April 2, 2019 · 2 min · jiezi

CSS3开发文档

站点:前端开发文档原文:CSS选择器原文:CSS继承属性原文:CSS3核心模块原文:CSS盒子模型原文:CSS背景图像原文:CSS清除浮动原文:CSS定位CSS选择器并集:对选择器进行分组,被分组的选择器可以分享相同的声明。用逗号将需要分组的选择器开分。h1,h2,h3,h4,h5,h6{}交集:两种属性同属一个元素p.name{}、p#id{}、.name1.name2{}后代(派生):根据元素在位置上的关系定义样式,使用空格隔开,后代选择器尽量不要超过3个,不必把每个层级都列出,只需要写关键点即可li strong {}子代:只能选择作为某元素的子元素,只选择子代,往后孙子一代不选择h1 > strong {}兄弟和相邻兄弟:选择紧接在另一元素后的,并且二者有相同父元素h1 + p {}属性:对带有指定属性的HTML元素设置样式,权重为10属性选择器:为带有title属性的所有元素设置样式,[title] {}属性和值选择器:为title=“name"的所有元素设置样式,[title=name] {}设置表单的样式:input[type=“text”] {}伪类::active:被激活的元素:focus:有键盘输入焦点的元素:hover:鼠标悬停:link:未被访问的链接:visited:已被访问的链接:first-child:元素的第一个子元素:lang:带有指定lang属性的元素权重:div(1)class/类选择器(10)id(100)结构选择器(新增伪类(面试题)):not:排除:nth-child(n):第几个元素 从1开始设置:nth-child(2n):偶数元素 从0开始设置:nth-child(2n+1):奇数元素:nth-of-type(n):某个元素下同类型的第几个元素:nth-last-child:倒数第几个元素:first-child->:nth-child(1)::fisrt-of-type:第一个同级兄弟元素:last-of-type:最后一个同级兄弟元素:nth-of-type(n):第几个同级兄弟元素:last-child:最后一个子元素:only-child:仅有一个子元素:only-of-type:只有一个同类型的子元素:empty:空内容:checked:被选中 主要用在input表单元素属性选择器E[attr=val]:E[attr|=val]:只能等于val 或只能以val-开头E[attr*=val]:包含val字符串E[attr~=val]:属性值有多个,其中一个是valE[attr^=val]:以val开头E[attr$=val]:以val结尾目标伪类选择器:target():用来匹配URL指向的目标元素(存在URL指向该匹配元素时,样式效果才会生效)伪元素::first-line:匹配首行文本,只能用于块级元素:first-letter:匹配首字符:before/:after:DOM元素前后插入额外的内容遇到伪元素before/after就要加上content=‘‘display: block;:独占一行display: inline-block;:不独占一行CSS继承属性无继承性的属性display:规定元素应该生成的框的类型文本属性:vertical-align:垂直文本对齐text-decoration:添加文本装饰text-shadow:文本阴影效果white-space:空白符的处理unicode-bidi:设置文本的方向盒子模型的属性:width、heightmargin、margin-top/right/bottom/leftborder、border-top/right/bottom/leftborder-style、border-top/right/bottom/left-styleborder-width、border-top/right/bottom/left-widthborder-color、border-top/right/bottom/left-colorpadding、padding-top/right/bottom/left背景属性:backgroundbackground-colorbackground-imagebackground-repeatbackground-positionbackground-attachment定位属性:floatclearpositiontop/right/bottom/leftmin-width/min-height、max-width/max-heightoverflowclipz-index生成内容属性:contentcounter-resetcounter-increment轮廓样式属性:outline-styleoutline-widthcounter-coloroutline有继承性的属性字体系列属性font:组合字体font-family:字体系列font-weight:字体粗细font-size:字体尺寸font-style:字体风格font-variant:小写字母转换为大写,字体尺寸更小font-stretch:对当前font-family进行伸缩变形。所有主流浏览器不支持。font-size-adjust:为某个元素规定一个aspect值,保持首选字体的x-height文本系列属性text-indent:文本缩进text-align:文本水平对齐line-height:行高word-spacing:字间距letter-spacing:字符间距text-transform:控制文本大小写direction:文本书写方向color:文本颜色元素可见性:visibility表格布局属性caption-sideborder-collapseborder-spacingempty-cellstable-layout列表布局属性list-style-typelist-style-imagelist-style-positionlist-style生成内容属性:quotes光标属性:cursor页面样式属性pagepage-break-insidewindowsorphansCSS3核心模块过渡动画transition:过渡动画transition-property:过渡属性 all[attr]transition-duration:过渡时间transition-delay:延迟时间transition-timing-function:运行类型ease:(逐渐变慢)默认值linear:匀速ease-in:加速ease-out:减速cubic-bezier:贝塞尔曲线过渡动画效果思考步骤:找到过渡属性找到过渡属性起始值和结束值在合适的位置上增加transition属性2D变换transform:变形属性rotate():旋转函数deg:度数transform-origin:旋转的基点skew():倾斜函数skewX()skewY()scale():缩放函数 默认值是1scaleX()scaleY()translate():位移函数translateX()translateY()animation-声明关键帧关键帧——@keyframes类似于flash定义动画在每个阶段的样式,即帧动画关键帧的时间单位数字:0%、25%、100%等(设置某个时间段内任意时间点的样式)字符:from(0%)、to(100%):格式@keyframes:动画名称{动画状态}animation-调用动画必要属性animation-name:动画名称(关键帧名称)animation-duration:动画执行时间可选属性:animation-timing-functionlinear:匀速ease:缓冲ease-in:由慢到快ease-out:由快到慢ease-in-out:由慢到快再到慢ease-bezier(num,num,num,num):特定的贝塞尔曲线类型,4个数值需在[0,1]区间内animation-delay:动画延迟animation-iteration-count:重复次数animation-direction:动画运行的方向 normal|reverse|alternate|alternate-reverseanimation-play-state:动画状态 running|pausedanimation-fill-mode:动画结束后的状态 none|forwards|backwards|both3D变换transform-style: flat|preserve-3d:3D空间展示perspective:景深效果transform: persective(800px):直接作用在子元素上transform:新增函数translate3d(tx, ty, tz):translateX() translateY() translateZ()rotate3d(rx, ry, rz, a):rotateX() rotateY() rotateZ()scale3d(sx, sy, sz):sacleX() sacleY() sacleZ()圆角 border-radiusborder-radius:1-4个数字/1-4个数字水平半径/垂直半径四个数字方向分别是左上 右上 右下 左下没有/则水平半径和垂直半径一样border-radius: 10px/5px;border-radius: 60px 40px 30px 30px/30px 20px 10px 5px例子:圆 椭圆 半圆 扇形线性渐变 linear-gradientlinear-gradient:([<起点>||<角度>,]?<点>,<点>…)只能用在背景上颜色是沿着一条直线轴变化参数起点:开始渐变方向角度:开始渐变角度点:渐变点的颜色和位置重复线性渐变径向渐变 radial-gradientradial-gradient从“一点”向多个方向颜色渐变shape形状:ellipse、circle或设置水平半径,垂直半径size:渐变的大小,即渐变停止位置:closet-side:最左边farthest-side:最远边closet-corner:最近角farthest-corner:最远角(默认值)position:关键词|数值|百分比重复的径向渐变背景background-originpadding-box:从padding区域显示border-box:从border区域显示content-box:从content区域显示background-clippadding-box:从padding区域向外裁剪border- box:从border区域向外裁剪content-box:从content区域向外裁剪text:文本裁剪background-size100% 100%:百分比10px 10px:数值contain:按原始比例收缩,背景图显示完整,但不一定铺满整个容器cover:按原比例收缩,背景图可能显示不完整,但铺满整个容器background-attachment背景图片是滚动/固定 fixed(固定的) 默认是滚动的盒子阴影box-shadow:h v blur spread color inset;h:水平方向偏移v:垂直方向偏移blur:模糊半径spread:扩展半径color:颜色inset:内阴影,默认是外阴影文本阴影text-shadow:x y blur colorx轴偏移 y轴偏移 模糊度 颜色多层阴影制作文字立体效果,设置多种颜色,中间以逗号隔开文字添加边框text-stroke:2px blue通过设定1px的透明边框,可以让文字变得平滑颜色设成透明能够创建镂空字体滤镜-webkit-filter:normal;:正常-webkit-filter:grayscale(1);:灰度,取值范围0-1-webkit-filter:brightness(0);:亮度,取值范围0-1-webkit-filter:invert(1);:反色,取值范围0-1,0为原图,1为彻底反色-webkit-filter:sepia(0.5);:叠加褐色,取值范围0-1-webkit-filter:hue-rotate(30deg);:色相(按照色相环旋转,顺时针方向)(红-橙-黄-黄绿-绿-蓝绿-蓝-蓝紫-紫-紫红-红)此处为叠加黄色滤镜-webkit-filter:saturate(4);:饱和度,取值范围0-,0为无饱和度,1为原图,值越高,饱和度越大-webkit-filter:contrast(2);:对比度,取值范围0-,0为无对比度(灰色),1为原图,值越高对比度越大-webkit-filter:opacity(0.8);:透明度,取值范围0-1,0为全透明,1为原图遮罩mask-image:mask-position:mask-repeat:CSS盒子模型border边框三角形箭头:正方形的任意相邻两条边,然后旋转一定的角度,得到我们需要的任意方向的箭头border、border-width、border-style、border-color三角形:border的3个属性:border-width/border-style/border-color,宽度和高度都为0,三角形箭头方向设定颜色,其余方向颜色设为透明transparentmargin边距margin边距重叠: 取大值,不是两者相加之和。margin-top的传递:大盒嵌套小盒,小盒加margin-top值,传递到大盒,导致整体下移。解决margin的兼容性问题:float: left;overflow: hidden;padding-top: 0/1px;border-top: 1px solid transparent;CSS背景图像background背景主要属性:background-color:背景颜色,简写background不能继承,默认是transparentinherit 指定背景颜色,从父元素继承background-image:背景图片url:图片URL地址node:默认值 背景上未放置图片inherit:指定背景图片从父元素继承一个元素可以引入多张背景图片;指定要使用的一个或多个背景图片,默认情况下,background-image放置在元素的左上角,并重复垂直和水平方向属性不能继承background-repeat:背景重复默认重复background-image的垂直和水平方向repeat 默认repeat-x 只有水平位置重复repear-y 只有垂直位置重复no-repeat 不重复inherit 从父元素继承background-position:背景定位设置背景图片的起始位置x、y 水平位置,垂直位置。左上角是0。单位(px,关键字,百分数)关键字成对出现left right top bottom center,仅指定一个关键字,其他值将会是center只设定x轴方向,默认y轴为centerinherit 从父元素继承background-attachment:背景关联设置背景图片固定或随页面的其余部分滚动scroll 默认fixed 固定inherit 从父元素继承background-size:背景图像的尺寸大小<length> 长度值指定图像大小。不允许负值<percentage> 百分比指定图像大小。不允许负值auto 图像的真实大小cover 将背景图像等比例缩放到完全覆盖容器,有可能超出容器contain 等比例所放到宽/高与容器的宽/高相等,背景图像始终被包含在容器内background-origin:设置背景图像的参考原点(位置)padding-box:从padding区域(含padding)开始显示背景border-box:从border区域(含border)开始显示背景content-box:从content区域开始显示背景background-clip:设置对象的背景图像向外裁剪的区域padding-box:从padding区域(不含padding)开始向外裁剪背景border-box:从border区域(不含border)开始向外裁剪背景content-box:从content区域开始向外裁剪背景text:从前景内容的形状(比如文字)作为裁剪区向外裁剪,实现使用背景作为填充色之类的遮罩效果。雪碧图:background-position: x yCSS清除浮动overflow: hiddenoverflow溢出隐藏清除浮动解决margin-top的传递问题(面试题):单行文本出现省略号(4个必备条件,缺一不可)width 宽度(不写宽度,默认继承父元素宽度)overflow: hidden;(溢出隐藏)white-space: nowrap;text-overflow: ellipsis;文字隐藏的方式,以省略号的方式隐藏多行文本出现省略号(必备条件,主要应用在移动端)display: -webkit-box; 弹性盒模型-webkit-box-orient: vertical;规定元素的排列方式:垂直排列-webkit-line-clamp: 2;:文字的行数(自定义)overflow: hidden;溢出隐藏多个元素在一行显示的方法display: inline;display: inline-block;float: left/right;display: inline-block;元素的特点盒子横向排列verticle-align属性会影响inline-block元素,值可能会设为top需要设置每一列的宽度如果HTML源码中元素间有空格,列与列之间会产生空隙解决方法:如果元素添加了dispay: inline-block;,父元素增加一个属性font-size: 0;,同时在元素本身增加font-size属性进行覆盖display:inline-block;在IE6/7下不兼容的解决方法增加display: inline; zoom: 1;属性IE7下块元素兼容display: inline-block;写法?直接让块元素设置为内联对象(设置属性 display: inline;),然后触发块元素的layout(如:zoom: 1;等)。兼容各浏览器的代码如下:div {display: inline-block; *display: inline; *zoom: 1;}float浮动float元素的特点在一行显示设置属性值为left时,浮动元素依次从父级盒子的左侧向右排列自动具有块级元素的属性,不需要添加display: block;脱离文档流子元素不会继承浮动属性浮动元素下面的元素不能识别浮动元素的高度和位置,占据浮动元素的位置所有的元素都可以使用浮动属性文档流和脱离文档流文档流:元素排版布局过程中,元素自动从左往右,从上往下的流式排列。每个非浮动元素块级元素独占一行,浮动元素按规则浮在行的一端。当前行容量满则另起一行浮动。内联元素不会独占一行几乎所有元素(包括块级、内敛和列表元素)均可生成子行,用于摆放子元素标准文档流等级:分为两个等级,块级元素和行内元素脱离文档流:文档流内的正常元素识别不到这个元素(脱离文档流的元素相当于平行漂浮于文档流之上)float元素产生的影响父元素设置背景颜色background-color不起作用父元素设置内边距属性padding不会被撑开父元素设置边框属性border不会被撑开清除浮动float清除浮动的方法给浮动元素的父级元素添加固定的高度height(不推荐)给浮动元素的父级元素添加溢出隐藏属性overflow: hidden;;给最后一个浮动元素后面添加一个块级元素,这个块级元素带有clear: both;属性clear清除浮动元素对文档流内元素的影响(可以让文档流内的元素识别到浮动元素的高度)left清除float为left的影响right清除float为right的影响both清除float所有的影响inherit从父级元素上继承该属性值clearfix清除浮动(固定代码)利用伪元素:after清除浮动必备条件,缺一不可display: block;确保元素是一个块级元素clear: both;不允许左右两边有浮动对象content: ‘’;伪元素:brfore/:after自带的属性,如果不写,伪元素不起作用写全的样式属性;不是必备条件height: 0; 防止在低版本浏览器中默认height: 1px;的情况,用height: 0;去覆盖font-size: 0; 字体大小overflow: hidden; 溢出隐藏visibility: hidden; 让所有可见性的元素隐藏overflow: hidden;和visibility: hidden;有什么区别?(面试题):如何让一个元素消失?opacity: 0;[0-1] 透明度display: none; 隐藏widht/height/line-height + overflow:宽/高/行高 + 溢出隐藏visibility: hidden;让所有可见性的元素隐藏clear: both;的特点元素需要是块级元素元素不能带有浮动属性元素必须放在最后一个浮动元素的后面CSS定位相对定位-position: relative;没有脱离文档流参照物是元素本身位置当top和bottom同时有值的情况下,top值生效,支持负值当left和right同时有值的情况下,left值生效,支持负值任何元素都可以设置相对定位属性相对定位元素位移发生改变,但元素原来的位置还会被占用,其他元素还是正常识别这个元素原来的位置绝对定位-position: absolute;脱离文档流可以设置参照物,参照物必须是其父级元素(直系父级),如果没有直接父级会一直往上查找直到找到最外层的根元素为止;有宽度和高度的情况下,top和bottom同时有值,top生效;left和right同时有值,left生效。没有宽度和高度的情况下,top和bottom同时设置值的情况下,会将这个盒子拉大,上下值都起作用,左右同理。可以设置层级关系z-index属性,必须要和定位元素(绝对,相对,固定)同时使用,才会起作用。一个元素定位在另一个元素上或者两个元素叠加的情况,都可以使用定位(绝对定位)绝对定位一定要设置相对参照物固定定位-position: fixed;脱离文档流参照物是浏览器的可视窗口任何元素都可以设置固定定位可设置top/bottom/left/right四个方位可通过z-index改变层级z-index属性的特点默认是书写顺序在后的定位元素覆盖顺序在前的定位元素可以使用z-index属性修改定位元素的层级关系所有定位元素的z-index默认值都一样z-index值是数字没有单位,支持负数一般都是同级元素进行层级的比较当参照物是相对定位或绝对定位的时候,父级元素之间没有z-index值,子元素的z-index值进行比较 ...

March 20, 2019 · 1 min · jiezi

SMTC:纯CSS 星星打分评价

星星打分评价的页面功能,需求上经常会碰到。网上搜一下,Js + CSS,纯 CSS 的版本也非常多。觉得搜到的都不理想,下意识觉得应该能有更好的实现自己动手尝试一下,果然能更好用所以开篇文章,分享一下 ^_^先来 HTML 代码感受下<!– 星星后面不需要文本的版本 –><!– 不需要 label,不需要 label.for,不需要 input.id –><div class=“star”> <input type=“radio” name=“rdStar” value="" checked=“checked”/> <input type=“radio” name=“rdStar” value=“1”/> <input type=“radio” name=“rdStar” value=“2”/> <input type=“radio” name=“rdStar” value=“3”/> <input type=“radio” name=“rdStar” value=“4”/> <input type=“radio” name=“rdStar” value=“5”/></div><!– 星星后面跟文本的版本 –><!– 只需要 label,不需要 label.for,不需要 input.id –><div class=“star”> <input type=“radio” name=“rdStarLabel” checked=“checked”/><label>请打分</label> <input type=“radio” name=“rdStarLabel” value=“1”/><label>一星</label> <input type=“radio” name=“rdStarLabel” value=“2”/><label>二星</label> <input type=“radio” name=“rdStarLabel” value=“3”/><label>三星</label> <input type=“radio” name=“rdStarLabel” value=“4”/><label>四星</label> <input type=“radio” name=“rdStarLabel” value=“5”/><label>五星</label></div> 只讨论现代浏览器,非现代浏览器请出门左转 需要的 CSS 基础总体没有用到很复杂或者罕见的 CSS 语法,对自己 CSS 水平有自信的可以跳过这一节A ~ B MDN 文档:组合器和选择器组通用兄弟选择器:匹配B元素,满足条件:B是A之后的兄弟节点中的任意一个(AB有相同的父节点,B在A之后,但不一定是紧挨着A)A + B MDN 文档:组合器和选择器组相邻兄弟选择器:匹配B元素,满足条件:B是A的下一个兄弟节点(AB有相同的父结点,并且B紧跟在A的后面)E:hover MDN 文档::hover()鼠标悬停在元素上时提供的样式E:checked MDN 文档::checked()处于选中状态的元素的样式,单选框<input type=“radio” />,复选框<input type=“checkbox” />,下拉选择select中的optionE:not(X) MDN 文档::not()匹配不符合参数选择器X描述的元素Data Uri MDN 文档:Data URLs允许嵌入小文件(通常用来嵌入小图片,但不只是图片哦)准备星星资源为了 WYCIWYG,我没有用独立图片,通过 Data Uri 是嵌入小图片.star input {background-image:url(‘data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAA8CAYAAABxVAqfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjJFRDE5Qjg0NTYwMTFFOTk5QzM5RjY0NTY0MDc0MEUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjJFRDE5Qjk0NTYwMTFFOTk5QzM5RjY0NTY0MDc0MEUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGMkVEMTlCNjQ1NjAxMUU5OTlDMzlGNjQ1NjQwNzQwRSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGMkVEMTlCNzQ1NjAxMUU5OTlDMzlGNjQ1NjQwNzQwRSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtLQhvIAAAOnSURBVHja7FdbSBRRGHbKSizKzIzNagnSpBvkbmEPXSiK6Lb0EEJEkkUURNFD0IOvQdAVFgoRErqgQQ/RDYSEjKCLuz5EJElEkZRb2YpGaBe371/+ibPTnHXO3CRw4OPMnDlzvvnPOf/l01KpVM5/dYVisYPAIrvfazZJS9B0AZ+AQDwcHlKdY4zNHz7HbTGw0ReLYW0AzQehK8FWp7y2+IzheQaw3lOLYS2RdJu8+giUqFitavEpST8t/1pPLIa10/kUy673QNCq1SoWnxzm/WxgtS2LYRU95wNkXRBYACwD1gBzLcxH/hwHHgHtwCteiSRWYjCDGGTL0e7jvy3zMNj9Ap4BreQZRHwHN5t9jrgR2uPtwBMfSWux7Lc03ttcNPdVDofN6xhIT6f3WE+L4Xh8LJp7wAaPSA/HQqHo38Ml5mOQ09LfBLa6THoApHUZp9pYCDB5E7DDJdIakDb848dmFQjIae8vA7scku7EnjaacZgSa5pGB24cbn84IG0FKQWeHDOObCFzkkNrl9qN1XMcEk/Gqo23Q7zEhYNVZId4lQvE8+wQuxG/K5SIOYQGskxYD0zjtBnPMk5aleRK+gsl/TeAQ3CTBD9/JbfHj4bRXjNJq+ukhYAkgFSieSx0tQB7QfguS2mkcZK5SoWf8CoPkWvQqsVBbtuAahB2DLeZXGs9wA9QCbSFI18BuRXw2arFeWhmYrI3DrQVZbty4CUsTlkiHlWLo2pxpNXiJSb1XS1OBVZwgeibWoyCpBdts37A/FKLhSBOcj9Fqdt+qMULOilftNRf/FCLRSDuMXwX4XrcM7VYjwn2m2wDrVwPJwVHarGXB5CYew68Ztfpk+0hFw8FnBLLuMpcCVSaZMGIxvXzQx5A12JM/sLluH4EzXlBLZ4wU4vfgFK87HaJVN//TLVoyJ+6Whxg8i6HpFWsw9JqEfNFZadaVIskX+Zj8FubpHs4rKbVIuapyxpAmFxXi3QSF+KjTtVcTf6uq0V832ApcrE/62pxiH03aZF0E5q7olq0HLnYZWqEMb8VDP4pqMVGOyFTVIv9CsSdbqnFNsUkn3BLLTZL9nIWkG+yTQPsjo7V4lNjegSaOPj34/6oiWUtbqjFDiacAlzknFwlfH+Wf6Cag5BIXKFUCHAI1U9nObtVrUEtHud66wp9wv19wG5gAnCdCgQs/TYV4mLhkGRTi/p4mVr8jrETVYhdVYvGImBk1aLk791Si6VU/pjFgT8CDAD9y6CbITrTjwAAAABJRU5ErkJggg==’);}实际图片: 宽 30 x 高 60,每个样子的图片 30 x 30注意以下细节要求:(配合CSS)1 小图片以列方式竖着依次保存2 第一个是选中样子,第2个是未选中样子* WYCIWYG :What You Copy Is What You Get基本原理Q:这个功能最重要最基础的部分是什么呢?A:鼠标滑过,星星选中和取消<style>.star { font-size:30px;}.star input { display:block; /* 和 inline-block 的恶心留白说再见 / float:left; / 多个星星排排坐,肩靠肩 / margin:0;padding:0; / 重置 input 默认样式 / width:1em;height:1em; / em 单位 棒!棒!棒! / font:inherit; / 有这个 em 单位才能棒起来 / background:center 0/cover no-repeat; / 准备好背景样式 / outline:0 none transparent; / 再见! Chrome 的默认黄框框 / -webkit-appearance:none;-moz-appearance:none;appearance:none; / 取消浏览器对 input 的默认渲染 /}.star input:hover ~ input { / 样式 .star input 把所有 input 元素 背景图 默认设为了选中样式 当前 hover 的 input 元素之后的所有兄弟 input 元素,背景图调整为未选中样式 —————– default | ★★★★★ hover | ✪ style | ★★★☆☆ ——————— Final | ★★★☆☆ / background-position:center -1em;}/ 请在这里复制上面那段 data uri 的 CSS 哦,不然没有小星星 因为有样式优先级,位置错了也可能没有小行星哦*/</style><div class=“star”> <input type=“radio” name=“rdStar” value=“1”/> <input type=“radio” name=“rdStar” value=“2”/> <input type=“radio” name=“rdStar” value=“3”/> <input type=“radio” name=“rdStar” value=“4”/> <input type=“radio” name=“rdStar” value=“5”/></div>鼠标滑过,星星变动一目了然走过路过动图了解一下(更清楚的展示原理)But:鼠标没放上去的时候是全选中的星星? [黑人问号???]这部分只是基本原理,下面 实现详解 解决所有问题实现详解1 解决鼠标没放上去的时候是全选中的星星在最前面增加一个 input ,表示为 0星,这个 0星 input 可以隐藏不显示为了演示,这个章节里不隐藏 0星 input2 增加 星星对应文本每个 input 后面增加对应用 label 包起来的文本input float:left label float:right<style type=“text/css”>.star { /* 基本原理 已有注释 / display:inline-block;height:1em;line-height:1em;font-size:30px;}.star input { / 基本原理 已有注释 / display:block;float:left;margin:0;padding:0;width:1em;height:1em;font:inherit;background:center 0/cover no-repeat;outline:0 none transparent; -webkit-appearance:none; -moz-appearance:none; appearance:none;}.star input:first-child { / 为了演示效果,不隐藏,但恢复浏览器渲染 / -webkit-appearance:radio; -moz-appearance:radio; appearance:radio;}.star input:checked ~ input { / Style [C] 已选中的 input 元素之后的所有兄弟 input 元素,背景图调整为未选中样式 ——————— default | ★★★★★ checked | ✪ style [C] | ★★☆☆☆ ——————— Final | ★★☆☆☆ / background-position:center -1em;}.star:hover input:checked ~ input { / Style [HC] 鼠标悬浮在 star 元素上时,已选中的 input 元素之后的所有兄弟 input 元素,背景图调整为选中样子 主要作用,覆盖上一条,将 input 元素 背景图全部恢复到 选中样子 —————– default | ★★★★★ checked | ✪ style [C] | ★★☆☆☆ hover | ✪ style [HC] | ★★★★★ ——————— Final | ★★★★★ / background-position:center 0;}.star:hover input:hover ~ input { / Style [HH] 基本原理 已有注释 —————– default | ★★★★★ checked | ✪ style [C] | ★★☆☆☆ hover | ✪ style [HC] | ★★★★★ style [HH] | ★★★★☆ ——————— Final | ★★★★☆ / background-position:center -1em;}/ label 部分参照上面 inpu 的样式,试着自己理解 /.star label {display:none;float:right;}.star input:checked + label {display:block;}.star:hover input:checked + label {display:none;}.star:hover input:hover + label {display:block;}/ 请在这里复制上面那段 data uri 的 CSS 哦,不然没有小星星 因为有样式优先级,位置错了也可能没有小行星哦*/</style><div class=“star”> <input type=“radio” name=“rdStar” checked=“checked”/> <input type=“radio” name=“rdStar” value=“1”/> <input type=“radio” name=“rdStar” value=“2”/> <input type=“radio” name=“rdStar” value=“3”/> <input type=“radio” name=“rdStar” value=“4”/> <input type=“radio” name=“rdStar” value=“5”/></div><br /><div class=“star”> <input type=“radio” name=“rdStarLabel” checked=“checked”/><label>请打分</label> <input type=“radio” name=“rdStarLabel” value=“1”/><label>一星</label> <input type=“radio” name=“rdStarLabel” value=“2”/><label>二星</label> <input type=“radio” name=“rdStarLabel” value=“3”/><label>三星</label> <input type=“radio” name=“rdStarLabel” value=“4”/><label>四星</label> <input type=“radio” name=“rdStarLabel” value=“5”/><label>五星</label></div>More 6666 ~~~更多选择,更多欢乐 :)1 只能选一次,选完之后不能重新选择/* pointer-events 请自行搜索了解 /.star-once input:first-child:not(:checked) ~ input {background-position:center 0;pointer-events:none;}.star-once input:not(:first-child):checked ~ input {background-position:center -1em;}.star-once input:first-child:not(:checked) ~ label {display:none;}.star-once input:not(:first-child):checked + label {display:block;}2 已有默认选择,锁定只读,不能更改,用来展示/ pointer-events 请自行搜索了解 /.star-lock {pointer-events:none;}3 多种尺寸只要修改 star 上的字体大小/ 除了 star 其他都是 em 单位,背景图用了 cover 自由缩放,轻松愉快哦*/.star {font-size:50px;}完整代码<!DOCTYPE html><html lang=“en”><head> <meta charset=“UTF-8”> <title>Star</title> <style type=“text/css”> .star {position:relative;display:inline-block;line-height:1em;} .star input {display:block;float:left;margin:0;padding:0;width:1em;height:1em;font:inherit;background:center 0/cover no-repeat;outline:0 none transparent; -webkit-appearance:none; -moz-appearance:none; appearance:none; } .star label {display:none;float:right;margin-left:0.5em;} .star input:first-child { display:none; /* -webkit-appearance:radio;-moz-appearance:radio;appearance:radio; / } .star input:checked ~ input {background-position:center -1em;} .star:hover input:checked ~ input {background-position:center 0;} .star:hover input:hover ~ input {background-position:center -1em;} .star input:checked + label {display:block;} .star:hover input:checked + label {display:none;} .star:hover input:hover + label {display:block;} .star-once input:first-child:not(:checked) ~ input {background-position:center 0;pointer-events:none;} .star-once input:not(:first-child):checked ~ input {background-position:center -1em;} .star-once input:first-child:not(:checked) ~ label {display:none;} .star-once input:not(:first-child):checked + label {display:block;} .star-lock {pointer-events:none;} / 以上的 css 可写入统一样式表 以下的样式 可在用的地方分别处理,自定义 大小,星星图片 */ .star {font-size:30px;} .star input {background-image:url(‘data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAA8CAYAAABxVAqfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjJFRDE5Qjg0NTYwMTFFOTk5QzM5RjY0NTY0MDc0MEUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjJFRDE5Qjk0NTYwMTFFOTk5QzM5RjY0NTY0MDc0MEUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGMkVEMTlCNjQ1NjAxMUU5OTlDMzlGNjQ1NjQwNzQwRSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGMkVEMTlCNzQ1NjAxMUU5OTlDMzlGNjQ1NjQwNzQwRSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtLQhvIAAAOnSURBVHja7FdbSBRRGHbKSizKzIzNagnSpBvkbmEPXSiK6Lb0EEJEkkUURNFD0IOvQdAVFgoRErqgQQ/RDYSEjKCLuz5EJElEkZRb2YpGaBe371/+ibPTnHXO3CRw4OPMnDlzvvnPOf/l01KpVM5/dYVisYPAIrvfazZJS9B0AZ+AQDwcHlKdY4zNHz7HbTGw0ReLYW0AzQehK8FWp7y2+IzheQaw3lOLYS2RdJu8+giUqFitavEpST8t/1pPLIa10/kUy673QNCq1SoWnxzm/WxgtS2LYRU95wNkXRBYACwD1gBzLcxH/hwHHgHtwCteiSRWYjCDGGTL0e7jvy3zMNj9Ap4BreQZRHwHN5t9jrgR2uPtwBMfSWux7Lc03ttcNPdVDofN6xhIT6f3WE+L4Xh8LJp7wAaPSA/HQqHo38Ml5mOQ09LfBLa6THoApHUZp9pYCDB5E7DDJdIakDb848dmFQjIae8vA7scku7EnjaacZgSa5pGB24cbn84IG0FKQWeHDOObCFzkkNrl9qN1XMcEk/Gqo23Q7zEhYNVZId4lQvE8+wQuxG/K5SIOYQGskxYD0zjtBnPMk5aleRK+gsl/TeAQ3CTBD9/JbfHj4bRXjNJq+ukhYAkgFSieSx0tQB7QfguS2mkcZK5SoWf8CoPkWvQqsVBbtuAahB2DLeZXGs9wA9QCbSFI18BuRXw2arFeWhmYrI3DrQVZbty4CUsTlkiHlWLo2pxpNXiJSb1XS1OBVZwgeibWoyCpBdts37A/FKLhSBOcj9Fqdt+qMULOilftNRf/FCLRSDuMXwX4XrcM7VYjwn2m2wDrVwPJwVHarGXB5CYew68Ztfpk+0hFw8FnBLLuMpcCVSaZMGIxvXzQx5A12JM/sLluH4EzXlBLZ4wU4vfgFK87HaJVN//TLVoyJ+6Whxg8i6HpFWsw9JqEfNFZadaVIskX+Zj8FubpHs4rKbVIuapyxpAmFxXi3QSF+KjTtVcTf6uq0V832ApcrE/62pxiH03aZF0E5q7olq0HLnYZWqEMb8VDP4pqMVGOyFTVIv9CsSdbqnFNsUkn3BLLTZL9nIWkG+yTQPsjo7V4lNjegSaOPj34/6oiWUtbqjFDiacAlzknFwlfH+Wf6Cag5BIXKFUCHAI1U9nObtVrUEtHud66wp9wv19wG5gAnCdCgQs/TYV4mLhkGRTi/p4mVr8jrETVYhdVYvGImBk1aLk791Si6VU/pjFgT8CDAD9y6CbITrTjwAAAABJRU5ErkJggg==’);} </style></head><body>star&ensp;<div class=“star”> <input type=“radio” name=“rdStar” checked=“checked”/><label>请打分</label> <input type=“radio” name=“rdStar” value=“1”/><label>一星</label> <input type=“radio” name=“rdStar” value=“2”/><label>二星</label> <input type=“radio” name=“rdStar” value=“3”/><label>三星</label> <input type=“radio” name=“rdStar” value=“4”/><label>四星</label> <input type=“radio” name=“rdStar” value=“5”/><label>五星</label></div><br />once&ensp;<div class=“star star-once”> <input type=“radio” name=“rdStarOnce” checked=“checked”/><label>请打分</label> <input type=“radio” name=“rdStarOnce” value=“1”/><label>一星</label> <input type=“radio” name=“rdStarOnce” value=“2”/><label>二星</label> <input type=“radio” name=“rdStarOnce” value=“3”/><label>三星</label> <input type=“radio” name=“rdStarOnce” value=“4”/><label>四星</label> <input type=“radio” name=“rdStarOnce” value=“5”/><label>五星</label></div><br />lock&ensp;<div class=“star star-lock”> <input type=“radio” name=“rdStarLock”/><label>请打分</label> <input type=“radio” name=“rdStarLock” value=“1”/><label>一星</label> <input type=“radio” name=“rdStarLock” value=“2”/><label>二星</label> <input type=“radio” name=“rdStarLock” value=“3” checked=“checked”/><label>三星</label> <input type=“radio” name=“rdStarLock” value=“4”/><label>四星</label> <input type=“radio” name=“rdStarLock” value=“5”/><label>五星</label></div><br /><div class=“star”> <input type=“radio” name=“rdStarX” checked=“checked”/> <input type=“radio” name=“rdStarX” value=“1”/> <input type=“radio” name=“rdStarX” value=“2”/> <input type=“radio” name=“rdStarX” value=“3”/> <input type=“radio” name=“rdStarX” value=“4”/> <input type=“radio” name=“rdStarX” value=“5”/></div></body></html> ...

March 19, 2019 · 4 min · jiezi

css制作从下往上逐渐显示的div

html代码<div class=“div1”> <div class=“mask”> <div class=“div2”>我是div顶部</div> </div></div>其中div1是整个容器,div2是需要从下往上显示的div。如果只是改变height高度的话,会导致div从上往下慢慢显示,所以并不能直接设置div2的高度来达成效果,此时我们需要一个遮罩mask来帮助div2达成想要的效果。css代码 .div1{ width: 400px; height:400px; background: #ccc; position: relative; } .div2{ width: 200px; height: 400px; background: #0099CC; position: absolute; left: 0; bottom: 0; } .mask{ width: 200px; height: 400px; /* 高度是变量 / position: absolute; left: 0; top: 0; / top是变量 */ }这是动画之后的css,div已经在了它最后应该在的位置。 那么我这里让div2从下往上显示的想法就是:遮罩mask始终在div1最下方的同时增加height,div2固定高度的同时绝对定位至遮罩mask的底部。 只要满足mask.height + mask.top = div1.height,就不会让mask从外观来看是从下往上显示的。最后加上动画效果.mask{ animation: animate 5s ease infinite; overflow: hidden;}@keyframes animate{ from { height: 0px; top : 400px; } to { height: 400px; top : 0px; }}下面就是最后的成果: ...

March 18, 2019 · 1 min · jiezi

React Hooks 从入门到上手

前言楼主最近在整理 React Hooks 的一些资料,为项目重构作准备,下午整理成了这篇文章。如果之前没接触过相关概念,那么通过这篇文章, 你将会了什么是React Hooks , 它是做什么的 , 以及如何使用。下面我会用一个具体的例子来说明, 通过这个例子, 你将了解:如何使用 React Hooks如何用 React Class components 实现同样的逻辑快速开始先快速搭建一个项目:npx create-react-app exploring-hooksDemo in setStateimport React, { Component } from “react”;export default class Button extends Component { state = { buttonText: “Click me, please” }; handleClick = () => { this.setState(() => { return { buttonText: “Thanks, been clicked!” }; }); }; render() { const { buttonText } = this.state; return <button onClick={this.handleClick}>{buttonText}</button>; }}功能非常简单: 点一下按钮, 就更新 button 的 text。Demo in Hooks这里,我们将不再使用 setState 和 ES6 Class. 轮到我们的Hooks登场了:import React, { useState } from “react”;引入 useState 就意味着我们将要把一些状态管理置于组件内部, 而且我们的 React Component 将不再是一个 ES6 class, 取而代之的是一个简单的纯函数。引入 useState 之后,我们将从中取出一个含有两个元素的数组:const [buttonText, setButtonText] = useState(“Click me, please”);如果对这个语法有疑问, 可以参考 ES6 解构.这两个值的名字, 你可以随意取, 和 React 无关,但是还是建议你根据使用的目的取一个足够具体和清晰的名字。就比如上面写的, 一个代表是 buttonText 的 值, 另一个代表是 setButtonText 的 更新函数。给 useState 传入的是一个初始值, 比如, 这个按钮的最初要显示的是: Click me, please。这个简单的例子的代码全貌:import React, { useState } from “react”;export default function Button() { const [buttonText, setButtonText] = useState(“Click me, please”); function handleButtonClick() { return setButtonText(“Thanks, been clicked!”); } return <button onClick={handleButtonClick}>{buttonText}</button>;}下面我们将介绍如何使用 Hooks 获取数据。使用 React Hooks 获取数据在这之前, 我们都是在 componentDidMount 函数里调API:import React, { Component } from “react”;export default class DataLoader extends Component { state = { data: [] }; async componentDidMount() { try { const response = await fetch(https://api.coinmarketcap.com/v1/ticker/?limit=10); if (!response.ok) { throw Error(response.statusText); } const json = await response.json(); this.setState({ data: json }); } catch (error) { console.log(error); } } render() { return ( <div> <ul> {this.state.data.map(el => ( <li key={el.id}>{el.name}</li> ))} </ul> </div> ); }}这种代码大家想必都非常熟悉了, 下面我们用 Hooks 来重写:import React, { useState, useEffect } from “react”;export default function DataLoader() { const [data, setData] = useState([]); useEffect(() => { fetch(“http://localhost:3001/links/”) .then(response => response.json()) .then(data => setData(data)); }); return ( <div> <ul> {data.map(el => ( <li key={el.id}>{el.title}</li> ))} </ul> </div> );}运行一下就会发现,哎呦, 报错了, 无限循环:原因其实也非常简单, useEffect 存在的目的 和componentDidMount, componentDidUpdate, and componentWillUnmount是一致的, 每次state 变化 或者 有新的props 进来的时候,componentDidUpdate componentDidUpdate` 都会执行。要解决这个 “bug” 也非常简单, 给 useEffect 传入一个空数组作为第二个参数:useEffect(() => { fetch(“http://localhost:3001/links/”) .then(response => response.json()) .then(data => setData(data)); },[]); // << super important array关于 Hook 的详细信息可以参考: Using the Effect Hook看到这你可能会按捺不住内心的小火苗,要去重构项目,个人还不建议这么做,因为接下来的几个版本中可能会有变化, 就像Ryan Florence 建议的:Hooks are not the endgame for React data loading.Data loading is probably the most common effect in an app.Don’t be in a big hurry to migrate to hooks for data unless you’re okay migrating again when suspense for data is stable.Own your churn.— Ryan Florence (@ryanflorence) February 12, 2019无论怎么说, useEffect 的出现还是一件好事。能把 Hooks 用于 Render props 吗能显然是能的, 不过没什么意义, 比如把上面的代码改一下:import React, { useState, useEffect } from “react”;export default function DataLoader(props) { const [data, setData] = useState([]); useEffect(() => { fetch(“http://localhost:3001/links/”) .then(response => response.json()) .then(data => setData(data)); }, []); return props.render(data)}从外部传入一个render即可, 但是这样做毫无意义: Reack Hooks 本身就是为了解决组件间逻辑公用的问题的。定义你的 React Hook还是上面的例子,我们把取数据的逻辑抽出来:// useFetch.tsximport { useState, useEffect } from “react”;export default function useFetch(url) { const [data, setData] = useState([]); useEffect(() => { fetch(url) .then(response => response.json()) .then(data => setData(data)); }, [] ); return data;}在其他组件中引用:import React from “react”;import useFetch from “./useFetch”;export default function DataLoader(props) { const data = useFetch(“http://localhost:3001/links/”); return ( <div> <ul> {data.map(el => ( <li key={el.id}>{el.title}</li> ))} </ul> </div> );}React Hooks 的本质上面我们说到 Reack Hooks 本身就是为了解决组件间逻辑公用的问题的。回顾我们现在的做法,几乎都是面向生命周期编程:Hooks 的出现是把这种面向生命周期编程变成了面向业务逻辑编程,让我们不用再去关注生命周期:图片来源而且, 最新的React 中, 预置了大量的Hooks, 最重要两个的就是: useState and useEffect.useState 使我们在不借助 ES6 class 的前提下, 在组件内部使用 state 成为可能。useEffect 取代了 componentDidMount, componentDidUpdate, and componentWillUnmount, 提供了一个统一的API。除了这两个之外, 可以在官方文档中了解更多:一个显而易见的事实是, 过不来了多久, 我们就会有三种创建React components 的姿势:functional componentsclass componentsfunctional components with hooks作为一个 React 忠实粉丝, 看到这些积极的变化实在是令人感到愉悦。Hooks 更多学习资源还有很多帮助我们更好的学和掌握 React Hooks, 也在这里分享一下:首先还是官方文档: Introducing Hooks, Hooks at a Glance 是稍微深入一些的内容。然后是一个入门教程: Build a CRUD App in React with Hooks. 关于状态管理, 还有一个比较有趣的文章: useReducer, don’t useState比较有意思的是, 我们最后会大量使用 useReducer, 形势和 Redux 非常类似:function reducer(state, action) { const { past, future, present } = state switch (action.type) { case ‘UNDO’: const previous = past[past.length - 1] const newPast = past.slice(0, past.length - 1) return { past: newPast, present: previous, future: [present, …future], } case ‘REDO’: const next = future[0] const newFuture = future.slice(1) return { past: […past, present], present: next, future: newFuture, } default: return state }}这也从侧面证明了Redux 在社区中的影响力( 其实这两个东西的核心开发者是同一个人 )。总结Hooks 的出现简化了逻辑,把面向生命周期编程变成了面向业务逻辑编程,为逻辑复用提供了更多可能。Hooks 是未来的方向。大概就是这些, 希望能对大家有些启发和帮助。才疏学浅,行文若有纰漏,还请各位大大帮忙指正, 谢谢。 ...

March 16, 2019 · 3 min · jiezi

CSS3使用transform:translate3d在Chrome出现的文字模糊及解决

很多人都知道CSS中添加transform:translate3d(0,0,0)能起到一个GPU加速的效果,让动画更流畅。但是这也带来了副作用。1、文字模糊?下面是使用了transform:translate3d后,很明显的出现了文字模糊,看起来很不舒服;2、快速修复bug先不问底层是如何造成的,我们先来修复一下这个问题,只要我把里面带小数的参数改成整数的,那么这个问题马上解决,下图很清晰了3、微软Edge浏览器没问题4、为什么?我猜想这是和Chrome用的渲染引擎的工作方式有关,具体为什么,有空再详细研究吧。//TODO

March 15, 2019 · 1 min · jiezi

前端培训-初级阶段(5 - 8)

前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。我们要讲什么CSS选择器(基本、层级、属性、伪类、伪状态)CSS常用样式属性CSS3 过渡、变换、动画CSS3 3D场景搭建与应用CSS选择器(基本、层级、属性、伪类、伪状态)基本选择器选择器例子例子描述CSS规范级别ID 选择器#login选择 id=“login” 的元素1类别选择器.btn选择 class=“btn” 的所有元素1元素选择器div选择所有 div 标签1通配选择器选择所有标签2属性选择器[type]选择有 type 属性的所有元素2属性选择器[type=file]选择 type=“file” 且 全匹配 的所有元素2属性选择器[class~=file]选择有 class=“file” 且 多值匹配 属性的所有元素2属性选择器[type|=file]选择有 type 属性 且 值为 file 或 file- 为前缀的所有元素2属性选择器[type^=file]选择有 type 属性 且 file 开头 的所有元素3属性选择器[type$=file]选择有 type 属性 且 file 结尾 的所有元素3属性选择器[type=file]选择有 type 属性 且 包含 file 的所有元素3CSS规范级别代表 CSS 1、CSS 2.1、CSS Selectors Level 3、Selectors Level 4。[type|=file] 实际为 [type|=file] ,在表格中无法输入所以改成全角。(有会输入的可以告诉我~)组合选择器选择器例子例子描述CSS规范级别分组html,body选择 <html> 和 <body >1后代 空格ul li选择祖先元素为 <ul> 元素的所有 <li> 元素。1下级ul>li选择父元素为 <ul> 元素的所有 <li> 元素。2相邻兄弟div+div选择紧接在 <div> 元素之后的 <div> 元素。2兄弟h2~div选择在 <h2> 元素之后的所有 <div> 元素3后代选择器要注意嵌套问题如 ul{font-size: 1.5em;}下级选择器一般用在只希望子元素有,不希望更深层级有。伪类选择器选择器例子例子描述CSS规范级别:linka:link未被访问的链接1:visiteda:visited已被访问的链接1:hovera:hover鼠标正在按下的链接1:activea:active鼠标正在上面的链接1:focusinput:focus有焦点的input2:first-childdiv :first-child代表父元素的第一个子元素2:last-childdiv :last-child代表父元素的最后一个子元素3:nth-child(n)div :last-child代表父元素的最后一个子元素3:emptydiv:empty空的 div3:target:target匹配锚点元素3:disableddiv :last-child代表父元素的最后一个子元素3:checked:checked选中的 checkbox、radio、select3:not(selector)div:not(:empty)所有不为空的 div3:focus-withinform:focus-within高亮获得焦点的表单4a 标签使用伪类时要注意爱恨原则(LoVe/HAte):active 也常用来做 tab 选择:focus 单击、触摸、tab 都可以触发伪元素选择器选择器例子例子描述CSS规范级别::after.icon:after在标签里面的最后增加一个行内元素2::before.icon:before在标签里面的最前面增加一个行内元素2::placeholderinput::placeholder修改文本框的 placeholder 样式4你可能注意到第一列是双冒号,第二列是单冒号,放心不是我写错了,规范定义的是单冒号是伪类,双冒号是伪元素。但是浏览器厂商大哥不买账,嗯,目前来说单冒号会兼容性更好。after 和 before 需要 content: ‘内容’,不然会不显示。placeholder 属于 shadow DOM上面是一些我们常用,或者说用途比较大的选择器。一些兼容不好,新规范,鸡肋一些的我没写出来。有兴趣可以去 MDN 中看。差点忘记的权重计算(优先级)下面列表中,选择器类型的优先级是递增的:类型选择器(type selectors)(例如, h1)和 伪元素(pseudo-elements)(例如, ::before)类选择器(class selectors) (例如,.example),属性选择器(attributes selectors)(例如,[type=“radio”]),伪类(pseudo-classes)(例如, :hover)ID选择器(例如, #example)通配选择符(universal selector)(*), 关系选择符(combinators) (+, >, ~, ’ ‘) 和否定伪类(negation pseudo-class)(:not()) 对优先级没有影响。(但是,在 :not()内部声明的选择器是会影响优先级)。给元素添加的内联样式 (例如, style=“font-weight:bold”) 总会覆盖外部样式表的任何样式,因此可看作是具有最高的优先级。.当在一个样式声明中使用一个!important 规则时,此声明将覆盖任何其他声明。虽然技术上!important与优先级无关,但它与它直接相关。2. CSS常用样式属性这个就有点多了啊,数值单位px 绝对单位,像素,最常用的em 相对单位,相对于当前对象内文本的font-size的倍数rem CSS3 相对单位,相对于根元素(即html元素)的font-size的倍数vw vh CSS3 相对单位,屏幕视口,均分一百份vmax vmin CSS3 相对单位,屏幕视口,均分一百份,宽高最大、最小值0%/0px/0vw 可以省略单位写为 0忘了,还有 % ,这个单位留作课后作业吧。CSS 属性类型属性定位position / z-index / top / right / bottom / left / clip布局display / float / clear / visibility / overflow / overflow-x / overflow-y盒子-大小width / min-width / max-width / height / min-height / max-height盒子-外margin / margin-top / margin-right / margin-bottom / margin-left盒子-内padding / padding-top / padding-right / padding-bottom / padding-left边框border / border-width / border-style / border-color / border-top / border-top-width / border-top-style / border-top-color / border-right / border-right-width / border-right-style / border-right-color / border-bottom / border-bottom-width / border-bottom-style / border-bottom-color / border-left / border-left-width / border-left-style / border-left-color / border-radius / border-top-left-radius / border-top-right-radius / border-bottom-right-radius / border-bottom-left-radius / box-shadow / border-image / border-image-source / border-image-slice / border-image-width / border-image-outset / border-image-repeat背景background / background-color / background-image / background-repeat / background-attachment / background-position / background-origin / background-clip / background-size颜色color / opacity / <color> / Color Name / HEX / RGB / RGBA / HSL / HSLA / transparent / currentColor变换transform-origin / transform-style / perspective / perspective-origin / backface-visibility过渡transition / transition-property / transition-duration / transition-timing-function / transition-delay动画animation / animation-name / animation-duration / animation-timing-function / animation-delay / animation-iteration-count / animation-direction / animation-play-state / animation-fill-mode如上就是一些属性,还有一些放出来,感兴趣的可以去查一下。好了,下面我们简单介绍几个常用的display 属性none:隐藏对象。与visibility属性的hidden值不同,其不为被隐藏的对象保留其物理空间inline:指定对象为内联元素。block:指定对象为块元素。inline-block:指定对象为内联块元素。(CSS2)table-cell:指定对象作为表格单元格。类同于html标签<td>(CSS2)box:将对象作为弹性伸缩盒显示。(伸缩盒最老版本)(CSS3)inline-box:将对象作为内联块级弹性伸缩盒显示。(伸缩盒最老版本)(CSS3)flexbox:将对象作为弹性伸缩盒显示。(伸缩盒过渡版本)(CSS3)inline-flexbox:将对象作为内联块级弹性伸缩盒显示。(伸缩盒过渡版本)(CSS3)flex:将对象作为弹性伸缩盒显示。(伸缩盒最新版本)(CSS3)inline-flex:将对象作为内联块级弹性伸缩盒显示。(伸缩盒最新版本)(CSS3)position 属性static:常规流。此时4个定位偏移属性不会被应用。relative:常规流,位置不变,定位偏移属性移动的只是显示效果。absolute:脱离常规流,偏移属性参照的是离自身最近的定位祖先元素,如果没有定位的祖先元素,则一直回溯到body元素。盒子的偏移位置不影响常规流中的任何元素,其margin不与其他任何margin折叠。fixed:与absolute一致,但偏移定位是以窗口为参考。当出现滚动条时,对象不会随着滚动。(IE、iOS 有兼容问题)sticky:对象在常态时遵循常规流。它就像是relative和fixed的合体,当在屏幕中时按常规流排版,当卷动到屏幕外时则表现如fixed。该属性的表现是现实中你见到的吸附效果。(CSS3)center:与absolute一致,但偏移定位是以定位祖先元素的中心点为参考。盒子在其包含容器垂直水平居中。(CSS3)page:与absolute一致。元素在分页媒体或者区域块内,元素的包含块始终是初始包含块,否则取决于每个absolute模式。(CSS3)3. CSS3 过渡、变换、动画上面一节已经列出本节包含的属性了,这节我们讲一讲,具体应用。transition 过渡缩写形式 transition:property duration timing-function delay;,下面我们来分开说明一下属性名描述默认值transition-property执行过渡动作的属性alltransition-duration动作执行时间0transition-timing-function动作执行曲线 easeeasetransition-delay延迟执行动画的时间0 transition: border-color .5s ease-in .1s, background-color .5s ease-in .1s, color .5s ease-in .1s;等同于 transition-property: border-color, background-color, color; transition-duration: .5s, .5s, 1s; transition-timing-function: ease-in, ease-in, ease-in; transition-delay: .1s, .1s, .1s;意思是 border-color 的变化执行 0.5秒,使用 ease-in 曲线执行,等待 0.1秒后开始。意思是 background-color 的变化执行 0.5秒,使用 ease-in 曲线执行,等待 0.1秒后开始。意思是 color 的变化执行 1秒,使用 ease-in 曲线执行,等待 0.1秒后开始。transition-timing-function 说明这个属性的值比较有意思,可以做一些特别一些的动画。 图片来自MDN:timing-function。cubic-bezier() 定义了一条 立方贝塞尔曲线(cubic Béziercurve)。这些曲线是连续的,一般用于动画的平滑变换,也被称为缓动函数(easing functions)。一条立方贝塞尔曲线需要四个点来定义,P0 、P1 、P2 和 P3。P0 和 P3是起点和终点,这两个点被作为比例固定在坐标系上,横轴为时间比例,纵轴为完成状态。P0 是 (0, 0),表示初始时间和初始状态。P3 是(1, 1) ,表示终止时间和终止状态。变换transform 非常的灵魂,底层提供了 matrix(),matrix3d() 来操作,封装了 translate 移动、rotate 旋转 、scale 缩放、skew 斜切扭曲。刚才说的是 2D 的,加上 3D 就是 3D变换,如 translate3d()。有一个意外 perspective() 指定透视距离。transform-origin 默认值:50% 50%。用来设置变换的基准点。transform-style 默认值:flat ,默认是 2D 空间。设置为 preserve-3d 改成三维空间,元素将会创建局部堆叠上下文。动画过渡可以理解为两个关键帧的补间操作。动画就是一连串的关键帧。animation-name:动画名称,需要 @keyframes 来定义动画@keyframes testanimations { from { opacity: 1; } to { opacity: 0; } }.testanimations{ animation: testanimations 1s;}animation-duration:动画的持续时间animation-timing-function:动画执行曲线animation-delay:延迟的时间animation-iteration-count:循环次数。infinite:无限循环。<number>:指定对象动画的具体循环次数。animation-direction:在循环中是否反向运动 normal:正常方向(默认值) reverse:反方向运行 alternate:动画先正常运行再反方向运行,并持续交替运行 alternate-reverse:动画先反运行再正方向运行,并持续交替运行animation-fill-mode:结束时候的状态 none:默认值。不设置对象动画之外的状态 forwards:动画结束时的状态 backwards:动画开始时的状态 both:动画结束或开始的状态animation-play-state:动画暂停、开始状态。running:运动。paused:暂停。4. CSS3 3D场景搭建与应用主要应用 perspective ,下面我们将要做一个视差滚动的例子。往期前端培训-初级阶段(1 - 4)参考资料(引用) 培训目录出处-已备份到笔记MDNCSS速查视差滚动 ...

March 11, 2019 · 3 min · jiezi

前端 CSS : 6# 纯 CSS 实现时间线

介绍刚准备写的时候想着我 CSS 已经熟练了,随便写写应该就差不多了吧,15分钟才写了个半成品,还丑的很,一定是这十来天美誉写 CSS 的原因(甩锅)。所以还是安安分分的按照套路来吧,先写个 DIV,再把它填充,重复多个……感謝 comehope 大佬的 [前端每日实战]效果预览github.io 浏览源代码地址https://github.com/shanyuhai1…代码解读1. 基础的 HTML 结构<div class=“timeline”> <h1>文档时间线</h1> <div class=“cards”> <section class=“card”></section> </div></div>常规样式初始化:* { margin: 0; padding: 0; box-sizing: border-box;}body { height: 100vh;}h1 { text-align: center;}2. 基础 card 样式总体布局的 timeline 暂时不用考虑,首先完成 card ,之后将多个 card 组合时才需要考虑。card 分为两部分 header 存放标题,article 存放内容详情。修改 DOM 结构<section class=“card”> <header class=“card__header”> <div class=“header__number”> <span>1</span> </div> <h2 class=“header__title”> <span class=“title__date”>2019-03-09</span> <span class=“title__sub”>副标题</span> </h2> </header> <article class=“card__article”> <p>今天是个好日子</p> </article></section>修改 card 样式.card { position: relative; width: 400px; height: 200px; display: flex; flex-direction: column; box-shadow: 0px 3px 1px -2px rgba(0,0,0,0.2), 0px 2px 2px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12); /* 观测用,待删除 / border: 1px dashed darkorange;}这样一个基础的 card 就实现了。3. 完善 card 样式header 结构.card__header { display: flex; align-items: center; width: 100%; height: 35%; color: #fff; background-color: #134857; overflow: hidden;}header 内部.header__number { display: flex; align-items: center; justify-content: center; margin: 0 20px; padding: 6px 14px; font-size: 2rem; background-color: rgba(0,0,0,0.17);}.header__title { display: flex; margin-left: 10px; flex-direction: column; font-size: 0.6rem;}.title__sub { padding-top: 6px; font-size: 1.2rem;}article 结构article 初步的想法还是比较简单的:.card__article { width: 100%; height: 65%; background-color: #fff; border: 1px solid rgba(200,200,200,0.5); border-top: none; padding: 10px;}4. 多个 card修改 DOM 结构在 .cards 中增加多个 card ,此处仅显示 DOM 结构省略了部分内容。<div class=“timeline”> <h1>飞越高山与大洋的鱼</h1> <div class=“cards”> <section class=“card”> <header class=“card__header”> <div class=“header__number”> <span>1</span> </div> <h2 class=“header__title”> <span class=“title__date”>2019-03-09</span> <span class=“title__sub”>副标题</span> </h2> </header> <article class=“card__article”> <p>今天是个好日子</p> </article> </section> <section class=“card”></section> <section class=“card”></section> <section class=“card”></section> </div></div>.cards 时间线修改 .cards 、.card 大小,并利用伪元素增加中间线:.cards { position: relative; display: flex; flex-direction: column; width: 100%; height: 100%; / 观测用,待删除 */ border: 1px solid #000;}.cards::after { z-index: 9; content: “”; position: absolute; top: 0; bottom: 0; left: 50%; border-left: 1px solid rgba(200,200,200,0.5);}.card { width: 46%;}card 分列两侧.card:nth-child(odd) { align-self: flex-start;}.card:nth-child(even ) { align-self: flex-end;}增加三角标识利用 header 的伪元素实现.card:nth-child(odd) .card__header::after { position: absolute; left: 100%; content: “”; width: 0; height: 0; border-style: solid; border-width: 10px 0 10px 18px; border-color: transparent transparent transparent #134857;}.card:nth-child(even) .card__header::after { position: absolute; right: 100%; content: “”; width: 0; height: 0; border-style: solid; border-width: 10px 18px 10px 0; border-color: transparent #134857 transparent transparent;}中间线上增加标识点.card:nth-child(odd) .card__header::before { z-index: 10; position: absolute; left: calc(111.11% - 2.5px); content: “”; width: 10px; height: 10px; border-radius: 10px; background-color: #bdbdbd; box-shadow: 0 0 2px 6px #fff;}.card:nth-child(even) .card__header::before { z-index: 10; position: absolute; right: calc(111.11% - 5px); content: “”; width: 10px; height: 10px; border-radius: 10px; background-color: #bdbdbd; box-shadow: 0 0 2px 6px #fff;}5. 美化去除 border 注释直接删除即可。header 赋予不同颜色设置默认色::root { –bg-color: #10aec2; }.card__header { background-color: var(–bg-color);}.card:nth-child(odd) .card__header::after { border-color: transparent transparent transparent var(–bg-color);}.card:nth-child(even) .card__header::after { border-color: transparent var(–bg-color) transparent transparent;}修改为 4 种颜色循环(若想要循环更多颜色增加 n 即可):.card:nth-child(4n) { –bg-color: #10aec2;}.card:nth-child(4n+1) { –bg-color: #fbc82f;}.card:nth-child(4n+2) { –bg-color: #74759b;}.card:nth-child(4n+3) { –bg-color: #346c9c;}修改间隔h1 { margin-bottom: 10px;}.cards { padding: 10px 16px;}最后其实这个只完成了一半,还有进一步适配移动端还未完成,之后会补上。参考资料取色参考样式 ...

March 9, 2019 · 2 min · jiezi

关于webpack 打包后部分css样式被移除问题

webpack打包会移除一些旧的css代码,还原代码需操作如下/ autoprefixer: off /-webkit-box-orient: vertical;/ autoprefixer: on /

March 7, 2019 · 1 min · jiezi

Web 开发学习笔记(6) --- 前端开发之 HTML5

前言通过之前的文章, 我们已经搭好了 webapp 的雏形, 不过到目前为止, 我们的前端界面比较简单, 为此, 我们要学习前端的知识, 然后对界面进行改进.前端的内容可以分为 HTML CSS JavaScript 三部分, 我们首先学习 HTMLHTML5简介什么是 HTML5 ———— 第五代 HTML 语言为什么学 H5所有主流浏览器都支持 h5, chrome, firefox, safari, IE9+H5 改变了用户与文档的交互方式, 尤其是多媒体, 替代了传统的 flash, 多媒体标签 video, audio, canvas增加了其他新特性语义特性、本地存储、网页多媒体(音视频), 二位三维变换, 特效(过渡, 动画)相比于 h4, 抛弃了不合理不常用的标签和属性, 增加了一些标签和属性, h5代码更加简洁(<!doctype html>, <meta charset=“UTF-8”)HTML5 语义化标签header, nav, main, article, aside, footer. 更多标签可以查看这里 HTML Tags语义化标签的例子html<!doctype html><html><head> <meta charset=“UTF-8”> <title>Title</title> <link rel=“stylesheet” href=“index.css”></head><body> <!– 语义化标签 –> <header>This is header</header> <nav>This is nav</nav> <main> <article>article</article> <aside>aside</aside> </main> <footer>footer</footer></body></html>css*{ /padding: 0px; margin: 0px;/}header{ width: 100%; height: 100px; background-color: red;}nav{ width: 100%; height: 36px; background-color: green;}main{ width: 100%; height: 240px; background-color: #ccc;}main > article{ width: 80%; height: 100%; background-color: purple; float: left;}main > aside{ width: 20%; height: 100%; background-color: pink; float: right;}footer{ width: 100%; height: 80px; background-color: skyblue;}参考资料w3schools, HTML黑马程序员 2018版Html5+Css3由浅入深教程 ...

February 26, 2019 · 1 min · jiezi

前端 CSS : 5# 纯 CSS 实现24小时超市

介绍原文链接感謝 comehope 大佬的 [前端每日实战]效果预览github.io 浏览源代码地址https://github.com/shanyuhai1…代码解读1. html 结构命名规则使用了 BEM<figure class=“street”> <div class=“market”> </div></figure>常规样式初始化* { margin: 0; padding: 0; box-sizing: border-box;}body { height: 100vh; overflow: hidden;}2. 街道背景街道背景分为两部分深蓝色的天空.street { height: 100vh; position: relative; display: flex; justify-content: center; align-items: flex-end; background-color: #0b2e4e; overflow: hidden;}黑色的地面.street::before { content: “”; position: absolute; bottom: 0; left: 0; right: 0; width: 100%; height: 25vh; background-color: #000000;}3. 超市超市, 首先创建一个长方形代表超市.market { position: relative; display: flex; width: 520px; height: 270px; background-color: #fffecc; border: 4px solid #333333;}接着给超市增加 24 HOURS 的标识, 需要在 market 下增加一个 span 标签<div class=“market”> <span class=“market__name”>24 hours</span></div>生意好的超市必然亮堂堂.market { box-shadow: 0 22px 110px 12px #f5efa1;}增大超市(正方形的上 border ), 用于存放标识.market { border-top-width: 50px;}将标识定位到上 border, 并给其添加发光样式.market__name { position: absolute; top: -38px; left: 20px; font-family: sans-serif; font-size: 1.4em; letter-spacing: 0.4em; color: #bdf8ff; text-transform: uppercase; text-shadow: 0px 0px 9px #95cfef;}再给该标识添加闪烁动画.market__name { animation: signboardFlashes 5s infinite alternate linear;}/* keyframes /@keyframes signboardFlashes { 0% { opacity: 1; } 35% { opacity: 1; } 36% { opacity: 0; } 37% { opacity: 1; } 70% { opacity: 1; } 72% { opacity: 0; } 73% { opacity: 1; } 74% { opacity: 0; } 75% { opacity: 1; } 100% { opacity: 1; }}接着需要给超市增加 4 扇门, 并基于基础的样式<div class=“market”> <span class=“market__name”>24 hours</span> <span class=“market__fold”></span> <span class=“market__fold”></span> <span class=“market__fold”></span> <span class=“market__fold”></span></div>.market__fold { position: relative; width: 25%; border: 8px solid #000000;}/ 此处无法在 .market__fold 使用 inset 替代 /.market__fold::before { content: “”; position: absolute; top: 0; left: 0; bottom: 0; right: 0; width: 100%; height: 100%; box-shadow: 0 0 2px 1px #f3f1d5;}门是感应门, 当有生物靠近才会打开, 所以打开的动画等一下完成4. 月亮在 market 中添加 moon<div class=“market”> <span class=“moon”></span></div>通过绝对定位将其移动到超市的上方, 并添加动画效果.moon { position: absolute; top: -125px; left: -50px; width: 40px; height: 40px; background-color: #ffffc9; border-radius: 50%; box-shadow: 0 0 20px 1px #ffffc9; animation: moonMoves 360s infinite alternate linear;}@keyframes moonMoves { 0% { transform: translate(0, 0); } 50% { transform: translate(300px, -10px); } 100% { transform: translate(600px, 0); }}5. 原地踏步的猫在 market 添加 cat<div class=“market”> <section class=“cat”></section></div>首先给予一个轮廓方便观察.cat { –cat-color: red; position: absolute; left: -200px; bottom: -24px; width: 46px; height: 30px; margin: auto; color: var(–cat-color); background-color: var(–cat-color); border-radius: 30px;}修改猫的 DOM 结构, 添加头, 尾巴, 四肢<section class=“cat”> <div class=“cat__head”></div> <span class=“cat__tail”></span> <span class=“cat__leg”></span> <span class=“cat__leg”></span> <span class=“cat__leg”></span> <span class=“cat__leg”></span></section>然后依次给予其样式首先是头部, 头部使用伪元素形成耳朵, 并添加头部晃动效果.cat__head { position: absolute; right: -10px; top: -8px; width: 24px; height: 24px; background-color: inherit; border-radius: 50%; animation: catMovesHead 0.3s infinite linear;}.cat__head::before, .cat__head::after { content: “”; position: absolute; top: 0; width: 10px; height: 10px; background-color: inherit; border-radius: 2px;}.cat__head::before { left: 2px; transform: rotate(16deg);}.cat__head::after { right: 2px; transform: rotate(-16deg);}@keyframes catMovesHead { 0% { transform: translateY(0); } 50% { transform: translateY(2px); } 100% { transform: translateY(0); }}添加尾巴及动画效果(尾巴由椭圆形的一半构成).cat__tail { position: absolute; left: -18px; top: -22px; width: 30px; height: 42px; border-radius: 50%; border: 7px solid var(–cat-color); border-left-color: transparent; border-bottom-color: transparent; transform-origin: right; animation: catMovesTail 0.3s infinite linear;}@keyframes catMovesTail { 0% { transform: rotate(0); } 50% { transform: rotate(-3deg); } 100% { transform: rotate(0); }}猫咪的四肢.cat__leg { position: absolute; bottom: -12px; width: 6px; height: 20px; background-color: inherit; border-radius: 3px; transform-origin: top;}将四肢分开和添加动画<span class=“cat__leg cat__leg–1”></span><span class=“cat__leg cat__leg–2”></span><span class=“cat__leg cat__leg–3”></span><span class=“cat__leg cat__leg–4”></span>.cat__leg–1, .cat__leg–2 { left: 5px;}.cat__leg–3, .cat__leg–4 { right: 5px;}.cat__leg–1, .cat__leg–3 { transform: rotate(24deg); animation: catMovesLegs 0.6s infinite linear;}.cat__leg–2, .cat__leg–4 { transform: rotate(-24deg); animation: catMovesLegs 0.6s infinite -0.3s linear;}@keyframes catMovesLegs { 0% { transform: rotate(36deg); } 50% { transform: rotate(-36deg); } 100% { transform: rotate(36deg); }}6. 猫过门开给予猫一个整体的动画.cat { animation: catRuns 20s infinite linear;}@keyframes catRuns { 0% { transform: translateX(0) rotateY(0); } 70% { transform: translateX(800px) rotateY(0); } 71% { transform: translateX(1000px) rotateY(180deg); } 100% { transform: translateX(0) rotateY(180deg); }}修改超市门的 DOM 上的 class<span class=“market__fold”></span><span class=“market__fold market__fold–left”></span><span class=“market__fold market__fold–right”></span><span class=“market__fold”></span>再增加开关门的效果, 根据上方猫的花费时间进行计算, 取一样的时间更方便一些.market__fold–left { animation: doorMovesLeft 20s infinite linear;}.market__fold–right { animation: doorMovesRight 20s infinite linear;}@keyframes doorMovesLeft { 0% { transform: translateX(0); } 28% { transform: translateX(0); } 30% { transform: translateX(-90%); } 54% { transform: translateX(-90%); } 56% { transform: translateX(0); } 83% { transform: translateX(0); } 85% { transform: translateX(-90%); } 97% { transform: translateX(-90%); } 99% { transform: translateX(0); } 100% { transform: translateX(0); }}@keyframes doorMovesRight { 0% { transform: translateX(0); } 28% { transform: translateX(0); } 30% { transform: translateX(90%); } 54% { transform: translateX(90%); } 56% { transform: translateX(0); } 83% { transform: translateX(0); } 85% { transform: translateX(90%); } 97% { transform: translateX(90%); } 99% { transform: translateX(0); } 100% { transform: translateX(0); }}最后就这样大功告成了不过记得把猫的颜色改回来/ 猫 */.cat { –cat-color: #000000;}7. 补充最后切换为移动端时注意到 超市会横向占满, 通过 padding 设置一个空隙即可.street { padding: 0 6vw 115px;}感想最近忙着学习 Koa, 结果 CSS 感觉忘得差不多了,唉 ...

February 26, 2019 · 4 min · jiezi

编写自适应高度的 textarea

文本框是很常见的输入控件,我相信只要写过表单的肯定接触过 textarea 这个元素。OK。但是现在产品经理说了:需要这个文本框可以根据用户输入内容自适应其高度。height: auto有些初学者可能会想:自适应高度不就是 height: auto 么?可是你想一下,一个 textarea 没有手工给它指定过样式,不应该就默认是 height: auto 么?但是它还是有自己的初始高度,并没有像一个 div 那样高度为 0。与 div 不同,textarea 的默认高度不是根据其内容自适应,而是由属性 rows 指定,其默认值是 2。rows 这个属性(Attribute)只接受正整数,指定其他值浏览器会忽略掉其值,比如你写 rows=“auto” 那么 rows 就是 2,rows=“0” 也是 2。所以指定 height: auto 是行不通的,height 属性必须人工指定其值。scrollHeight遇到过这个问题的同学(比如当初的笔者),肯定想到过 scrollHeight 这个 DOM 属性。想法很简单,当用户输入的文本超过了文本框自身高度时不是会出现滚动条嘛,那么自然而然就能想到 scrollHeight 这个属性。scrollHeight 就应该是用户输入文本的真实高度,至少超过文本框既定高度时是这样。那么问题来了:如果没超过呢?OK 我知道你会先指定 rows=“1” 让文本框默认高度只有一行。但是考虑这种情况:用户先输入了很多行文本然后删除了一段:scrollHeight 值没有变化。MDN 上说了:没有垂直滚动条的情况下,scrollHeight值与元素视图填充所有内容所需要的最小值clientHeight相同scrollHeight 确实会随着用户输入内容多少而增减,但是仅限于出现滚动条的情况,对于题设这个情况必然不适用,因为需求就是不能出现滚动条(严格来说是超出可视区域)。你可以在获取 scrollHeight 的值之前先把文本框高度设为 0 强制让滚动条出现,但是这样可能使页面发生闪烁,而且性能也低。split(’\n’)DOM 属性靠不住,那我自己算文本高度不行吗?说我拿出所有文本,按换行符拆分,看有多少行,行数 * 行高 不就是最终文本高度吗?额。。。当文本没有折行的情况下是这样。。。contenteditablecontenteditable 确实是一个(相对)可行的方案,但是作为一个踩过坑的先行者劝解你:不到万不得已,contenteditable 不要碰。这个玩意各个浏览器实现都不一样,各种奇葩行为,光一个换行符就足够折磨你半天。当然这里还没有到那么复杂的地步,但是你得先会把“复制——粘贴”过去的样式去掉才行笔者的方法说了那么多废话,那么究竟该怎么办呢?这里笔者提供一种方法。当然首先声明:笔者的方法未必是最简单的,如有其它更简单的方案欢迎留言提出。我们想一下,textarea 不能按照内容自适应高度,div 可以啊,能不能先把文本填到一个 div 里,div 的高度就应该是文本框所需高度(当 padding、line-height 等样式都一致的情况下),这时获取 div 的高度赋值给文本框高度不就行了吗。就是这样的思路。我们也不需要专门使用 JS 获取,只要让 div 把父元素撑起来,绝对定位 textarea 元素让文本框占满整个父元素大小就好了。直接上代码:<style> #parent { width: 500px; font: 12px monospace; position: relative; } #dummy { padding: 2px; border: 1px solid; visibility: hidden; } #dummy::after { content: “\A”; } #textarea { position: absolute; left: 0; right: 0; top: 0; bottom: 0; resize: none; width: 100%; font: inherit; }</style><div id=“parent”> <div id=“dummy”></div> <textarea id=“textarea” oninput=“document.getElementById(‘dummy’).textContent = this.value”></textarea></div>这里查看运行结果:https://codepen.io/CarterLi/p…三个要点:字体相关样式 #dummy 和 textarea 两元素必须完全一致,差一点就可能出现两者高度对不上的情况。#dummy 中 white-space: pre-wrap 醒目。否则会出现 HTML 中吞空格、换行符的情况。就算有了 white-space: pre-wrap,HTML 仍然会吞掉最后的换行符。解决方案是在 #dummy 最后插入一个换行符元素。可以是 <br />,也可以是一个伪元素。伪元素中换行符的写法是 \A(即换行符的 ASCII 码 10 的十六进制表示。不能写 \n)代码中是用 JS 给 #dummy 赋值。项目中如果你用 vuejs 或 angular 等 MVVM 框架,直接把文本框的值绑定到 div 上就好,非常方便。如果你要限制文本框的最大最小高度,在 #dummy 上直接设置 min-height max-height 即可。完最后说一句:把 textarea 盖到一个 div 上的做法还可以简单的实现文本框的语法高亮,读者可以想想怎么做。 ...

February 25, 2019 · 1 min · jiezi

2018 年终总结

职业篇博客之前博客系统一直用的 Ghost,然而 Ghost 的新版在vps上升级迁移遇到很多问题,后来索性自己用 nodejs 写了一个,基于 nextjs。翻译文章花了一周的闲余时间,翻译了 maintainable css,访问地址 http://maintainablecss.wtser.com/真的是好久没翻译文章了。生产力在项目中使用了 stylelint ,peritter 等工具,统一代码风格,提高代码质量。iterm2 插件https://xiaozhou.net/learn-the-command-line-iterm-and-zsh-2017-06-23.htmlaliasalias gs=“git status"alias gitclean=“git branch -vv | grep ’ gone]’ | awk ‘{print $1}’ | xargs git branch -d"快速跳转使用 d 这个命令,列出最近访问过的各个目录目录名简写与补全比如我们要进入到 ~/workspace/src/dict,我们只需要输入每个目录的首字母就行,然后再TAB键补全重复上一条命令输入 r ,可以很便捷的重复执行上一条命令。zsh-autosuggestionshttps://github.com/zsh-users/zsh-syntax-highlightinghttps://github.com/zsh-users/zsh-autosuggestions了解了一些 tmux 相关的知识点,但是缺少实践。http://harttle.com/2015/11/06…http://kumu-linux.github.io/b...http://cenalulu.github.io/lin...http://wdxtub.com/2016/03/30/...Tmux使用手册研究与学习算法学习经典排序算法总结与实现JS家的排序算法业务相关给自己取了一个英文名 English name Finn参与公司的一个SPA项目,react,学习了 redux state 相关的知识。SVG use with External Source<svg viewBox=“0 0 100 100”> <use xlink:href=“defs.svg#icon-1”></use></svg>https://css-tricks.com/svg-use-external-source/But, the external resource way doesn’t work in any version (up to 11 tested) of Internet Explorer. Even the ones that do support inline SVG: 9, 10, 11.https://github.com/jonathantneal/svg4everybodyIt works like this:If the browser is IE 9, 10, or 11 (User Agent sniff, but that’s the whole point here).Ajax for the SVG file referencedFind the needed bit, based on the ID referenced (e.g. #icon-1)Inject that into the <svg> on the page简单的跑马灯使用 picture 元素实现 图片的 responsesrcset only use for mobile imghttps://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture<picture> <source srcset=“mdn-logo-wide.png” media="(min-width: 600px)"> <img src=“mdn-logo-narrow.png” alt=“MDN”></picture>overscroll-behavior 解决 modal 滚动带动底下页面滚动问题比较拙的解决方案就是给body添加overflow:hidden,但这有可能会影响我们的操作,甚至影响你浏览你的页面。CSS的overscroll-behavior,这个属性有三个可取值:auto:其默认值。元素(容器)的滚动会传播给其祖先元素。有点类似JavaScript中的冒泡行为一样contain:阻止滚动链接。滚动行为不会传播给其祖先元素,但会影响节点内的局部显示。例如,Android上的光辉效果或iOS上的回弹效果。当用户触摸滚动边界时会通知用户。注意,overscroll-behavior:contain在html元素上使用,可以阻止导航滚动操作none:和contain一样,但它也可以防止节点本身的滚动效果> overscroll-behavior属性是overscroll-behavior-x和overscroll-behavior-y的简写,如果你只想控制其中一个方向的滚动行为,可以使用其中的某一个属性。编写自定义视频播放器https://www.w3.org/2010/05/video/mediaevents.htmlfullscreen apihttps://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_APIhttps://github.com/sindresorhus/screenfull.jsbug in safarielement:fullscreen{}针对被全屏的元素 (如果element是iframe,:-webkit-full-screen-ancestor:not(iframe) {} 在safari 不生效)safari iframe webkitIsFullScreen aways return falseevent webkitfullscreenchange e.target return document when exit fullscreen娱乐篇电影电视剧大佛普拉斯有话好好说 Keep Cool 1997燃烧底特律 我欲成人末代皇帝红海行动西部世界 S2诚邀辣妹:网络性与爱西游记女儿国this is us s2雌猫们奇迹男孩斯大林之死挚爱梵高异形:契约逃出绝命镇小丑回魂恐惧支配校合唱团的秘密短暂的婚姻去他妈的世界熔炉黑镜 第四季This is us season2移动迷宫2红高粱移动迷宫3逃离与神通行雷神3三块广告牌神秘巨星奇门遁甲大世界好极了中邪请以你的名字呼唤我相亲相爱唐人街探案2废城记电锯惊魂1-7江湖儿女美人计反贪风暴3爆裂无声升级动物世界沙海李银河的52堂性学课逃避可耻但有用血观音毒液特工李茶的姑妈影人生一串游戏青蛙旅行Getting Over It中国式父母阅读篇解忧杂货店What if二十世纪的教训根据我的看法,选举应该是这么回事:我们不是让新政府合法化,而是评判旧政府的表现如何——投票日那天是旧政府缴成绩单的时间。原子弹设计重来2人们的工作方式从“同步”协作变成了无须同步的协作。分布式员工团队(distributed workforce)最适合远程办公的脑力工作,比如写作、编程、设计、客户支持(先列举这几样),跟残酷的利润大战都没什么关系(制造业就属于这种)。我们发现,判断工作质量的最好方法,就是在正式雇用员工、给他们分配许多工作之前,先让他们试做一点。想要在工作中不被人忽略,有两种最基本的方法。第一个就是吵吵闹闹,弄出点动静来。第二个就是在工作中取得进展,交出漂亮作品。幸运的是,对于远程员工来说,工作质量最重要。学习之道活着自卑与超越侧重讲述婴幼儿时期父母教育对孩子的影响Your past experiences are blinding you你的经验会束缚你为啥会有 『偏见与歧视』只有放下执着才能去实现任何事情。It’s only after we’ve lost everything that we’re free to do anything.即便是那些大师级的人也无法找到最好的方式来取胜,因为他们擅长的地方束缚了他们的思想。Even these masters couldn’t see the best way to win because the one they knew so well colonised their mind.人们很难脱离自己的舒适区。处于对未知的恐惧,他们倾向于呆在自己熟悉的地方。People have a hard time letting go of their suffering. Out of a fear of the unknown, they prefer suffering that is familiar”如何摆脱偏见再次成为一个初学者Be a beginner again初学者的思想充满了许多的可能性,但是专家就少了许多In the beginner’s mind there are many possibilities, but in the expert’s there are few”初学者思想不是说要反对经验,而是在我们用经验应对新状况时应保持的一种开放的思想。Beginner’s Mind doesn’t mean negating experience; it means keeping an open mind on how to apply our experience to each new circumstance.未完待续… ...

February 23, 2019 · 2 min · jiezi

Web前端开发标准规范

web前端开发规范的意义提高团队的协作能力提高代码的复用利用率可以写出质量更高,效率更好的代码为后期维护提供更好的支持命名规则命名使用英文语义化,禁止使用特殊字符,禁止使用拼音,禁止使用中英文混合!项目、目录、html/css/js文件命名全部采用小写方式, 以下划线分隔。eg:my_project_nameclass、图片、视频、音频采用小写方式,以下划线或中划线分隔;eg:btn-play或btn_playid可采用驼峰命名;eg:play或playGame变量名采用小写方式, 以下划线分隔;应为名词,eg: car,person函数名采用驼峰命名,以动词开始。 getName(), 返回类型是布尔类型,一般以is开头,eg: isEnable();变量和函数命名,不要担心长度,一定要语义化合乎逻辑。

February 18, 2019 · 1 min · jiezi

四种方法实现──三栏布局(圣杯布局、双飞翼布局)

圣杯布局、双飞翼布局,本质上都是三栏布局──中间自适应两边固定宽。有一次面试,要求写出三种实现方式,结果只写出了两种,面试官说基础还不够扎实~呜圣杯布局圣杯HTML结构:<div class=‘main’> <div class=“middle”>中间的</div> <div class=“left”>左边的</div> <div class=“right”>右边的</div></div>双飞翼布局双飞翼HTML结构为:<div class=‘main’> <div class=“middle”> <div class=“inner_middle”>中间的</div> </div> <div class=“left”>左边的</div> <div class=“right”>右边的</div></div>一、float+margin<style type=“text/css”> .main{ overflow: hidden; background: #eee; } .left{ background: red; width: 200px; height: 280px; float: left; } .right{ background: blue; width: 200px; height: 290px; float: right; } .middle{ background: green; height: 300px; margin-left: 200px; margin-right: 200px; }</style> <div class=“main”> <div class=“left”></div> <div class=“right”></div> <div class=“middle”></div> </div>说明:网上还有人用padding替换margin的方法,感兴趣的自己去查。二、Position<style type=“text/css”> .main{ position: relative; } .left{ background: red; height: 300px; width: 200px; position: absolute; left: 0; top: 0; } .right{ background: blue; height: 300px; width: 200px; position: absolute; right: 0; top: 0; } .middle{ background: green; height: 300px; width: 100%; }</style> <div class=“main”> <div class=“left”></div> <div class=“center”></div> <div class=“middle”></div> </div>说明:网上有人提到这个方法在某些情况下会出现bug,具体没有深入了解过。三、Flex<style type=“text/css”> .main{ display: flex; align-items: center; } .left{ background: red; width: 200px; height: 300px; } .right{ background: blue; width: 200px; height: 300px; } .middle{ background: green; height: 300px; width: 100%; }</style> <div class=“main”> <div class=“left”></div> <div class=“middle”></div> <div class=“right”></div> </div>说明:低版本的浏览器有兼容的问题,在网上也看到有人用order控制位置四、Grid<style type=“text/css”> .main{ display: grid; height: 300px; } .left{ background: red; grid-row:1; grid-column:1/2; } .right{ background: blue; grid-row:1; grid-column:4/5; } .middle{ background: green; grid-row:1; grid-column:2/4; }</style> <div class=“main”> <div class=“left”></div> <div class=“middle”></div> <div class=“right”></div> </div>说明:grid-column一共分为5个格,“grid-column:1/2”占了第一个和第二个格,不是指占了二分之一。这个方法兼容性有比较大的问题,网上有不少文章提到浏览器还没有提供支持,实际上新版主流浏览器已经支持了。 ...

February 17, 2019 · 1 min · jiezi

知识整理之CSS篇

CSS篇主要从CSS兼容、CSS3新特性、CSS选择器、高频属性、高频布局、高频知识点、性能优化等方面进行归纳。如对HTML知识点感兴趣,可移步至:知识整理之HTML篇CSS HackCSS Hack就是针对不同的浏览器或不同版本浏览器写特定的CSS样式达到让浏览器兼容的过程。CSS Hack常见的有三种形式:CSS属性Hack、CSS选择符Hack以及IE条件注释Hack。CSS属性Hack(在标准模式下)color: red; /* 所有浏览器识别 /_color: red; / 仅IE6 浏览器识别 /-color: red; / 仅IE6 浏览器识别 /color: red; / 仅IE6、IE7 浏览器识别 /+color: red; / 仅IE6、IE7 浏览器识别 /+color: red; / 仅IE6、IE7 浏览器识别 /#color: red; / 仅IE6、IE7 浏览器识别 /color: red\0; / 仅IE8-IE10 浏览器识别 / color: red\9\0; / 仅IE9、IE10 浏览器识别 / color: red!important; / 仅IE6 浏览器不支持 / CSS选择符Hackhtml #demo { color: red; } /* 仅IE6 浏览器识别 /+html #demo { color: red; } /* 仅IE6、IE7 浏览器识别 /body:nth-of-type(1) #demo { color: red; } / IE9+、FF3.5+、Chrome、Safari、Opera 可以识别 /head:first-child+body #demo { color: red; } / IE7+、FF、Chrome、Safari、Opera 可以识别 /:root #demo { color: red9; } / 仅IE9 识别 /IE条件注释Hack<!–[if IE]>此处内容只有IE可见<![endif]–><!–[if IE6]>此处内容只有IE6.0可见<![endif]–><!–[if !IE 7]>此处内容只有IE7不能识别,其他版本都能识别,当然要在IE5以上。<![endif]–><!–[if gt IE 6]> IE6以上版本可识别,IE6无法识别 <![endif]–><!–[if gte IE 7]> IE7以及IE7以上版本可识别 <![endif]–><!–[if lt IE 7]> 低于IE7的版本才能识别,IE7无法识别。 <![endif]–><!–[if lte IE 7]> IE7以及IE7以下版本可识别<![endif]–><!–[if !IE]>此处内容只有非IE可见<![endif]–>常见浏览器兼容性问题与解决方案?不同浏览器的标签默认的padding和margin不同问题症状:常用标签,不加样式控制的情况下,各自的margin、padding差异较大。解决方案: { margin: 0; padding: 0;} 备注:这个是最常见的也是最易解决的一个浏览器兼容性问题,几乎所有的CSS文件开头都会用通配符来设置各个标签的margin、padding是0。块属性标签float后,又有横行的margin情况下,在IE6显示margin比设置的大问题症状:常见症状是IE6中后面的一块被顶到下一行。解决方案:在float的标签样式中设置#demo { display: inline } 当标签的高度设置小于10px,在IE6、IE7中会超出自己设置的高度问题症状:IE6、7和遨游里这个标签的高度不受控制,超出自己设置的高度。解决方案:给超出高度的标签设置#demo { overflow: hidden; }/ 或者 /#demo { line-height: 8px; } / 假设标签高度为9px / 行内属性标签,设置display:block后采用float布局,又有横行的margin的情况,IE6间距bug问题症状:IE6里的间距比超过设置的间距解决方案:#demo { display: block; display: inline; display: table;}备注:行内属性标签,为了设置宽高,我们需要设置display:block;(除了input标签比较特殊)。在用float布局并有横向的margin后,在IE6下,他就具有了块属性float后的横向margin的bug。不过因为它本身就是行内属性标签,所以我们再加上display:inline的话,它的高宽就不可设了。这时候我们还需要在display:inline后面加入display:talbe。图片默认有间距问题症状:几个img标签放在一起的时候,有些浏览器会有默认的间距,加了问题一中提到的通配符也不起作用。解决方案:img { float: left; }备注:因为img标签是行内属性标签,所以只要不超出容器宽度,img标签都会排在一行里,但是部分浏览器的img标签之间会有个间距。去掉这个间距使用float是正道。IE9一下浏览器不能使用opacity解决方案:#demo { opacity: 0.5; filter: alpha(opacity=50); filter: progid:DXImageTransform.Microsoft.Alpha(style = 0, opacity = 50); -moz-opacity: 0.5; -khtml-opacity: 0.5;}介绍一下标准的CSS的盒子模型?低版本IE的盒子模型有什么不同的?CSS盒子模型:由四个属性组成的外边距(margin)、内边距(padding)、边界(border)、内容区(width和height)盒子模型有两种, IE 盒子模型、W3C 盒子模型IE盒子模型宽高 = 内边距﹢边界﹢内容区标准盒子模型宽高 = 内容区宽高css设置方法/ 标准盒模型 /box-sizing: content-box;/ IE盒模型 /box-sizing: border-box;/ 继承父元素 */box-sizing: inherit;对BFC规范的理解?什么是BFCBFC(Block Formatting Context)即“块级格式化上下文”。常规流(也称标准流、普通流)是一个文档在被显示时最常见的布局形态。一个框在常规流中必须属于一个格式化上下文,你可以把BFC想象成一个大箱子,箱子外边的元素将不与箱子内的元素产生作用。BFC是W3C CSS 2.1 规范中的一个概念,它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。当涉及到可视化布局的时候,Block Formatting Context提供了一个环境,HTML元素在这个环境中按照一定规则进行布局。一个环境中的元素不会影响到其它环境中的布局。比如浮动元素会形成BFC,浮动元素内部子元素的主要受该浮动元素影响,两个浮动元素之间是互不影响的。也可以说BFC就是一个作用范围。形成BFC的条件浮动元素,float 除 none 以外的值定位元素,position(absolute,fixed)display 为以下其中之一的值 inline-block,table-cell,table-captionoverflow 除了 visible 以外的值(hidden,auto,scroll)BFC的特性内部的Box会在垂直方向上一个接一个的放置垂直方向上的距离由margin决定bfc的区域不会与float的元素区域重叠计算bfc的高度时,浮动元素也参与计算bfc就是页面上的一个独立容器,容器里面的子元素不会影响外面元素具体特性解释,可移步至CSS中的BFC详解什么是 FOUC?如何来避免 FOUC?什么是外边距重叠? 重叠的结果是什么?解释下什么是浮动和它的工作原理?什么是浮动?非IE浏览器下,容器不设高度且子元素浮动时,容器高度不能被内容撑开。 此时,内容会溢出到容器外面而影响布局。这种现象被称为浮动(溢出)。工作原理浮动元素脱离文档流,不占据空间(引起“高度塌陷”现象)浮动元素碰到包含它的边框或者其他浮动元素的边框停留如何清除浮动1. 给浮动元素的父元素添加高度(扩展性不好)如果一个元素要浮动,那么它的父元素一定要有高度。高度的盒子,才能关住浮动。可以通过直接给父元素设置height,实际应用中我们不大可能给所有的盒子加高度,不仅麻烦,并且不能适应页面的快速变化;另外一种,父容器的高度可以通过内容撑开(比如img图片),实际当中此方法用的比较多。2. clear:both在最后一个子元素新添加最后一个冗余元素,然后将其设置clear:both,这样就可以清除浮动。这里强调一点,即在父级元素末尾添加的元素必须是一个块级元素,否则无法撑起父级元素高度。<style>#wrap{ border: 1px solid;}#inner{ float: left; width: 200px; height: 200px; background: pink;}</style><div id=“wrap”> <div id=“inner”></div> <div style=“clear: both;"></div></div>3. 伪元素清除浮动上面那种办法固然可以清除浮动,但是我们不想在页面中添加这些没有意义的冗余元素,此时如何清除浮动吗?结合 :after 伪元素和 IEhack ,可以完美兼容当前主流的各大浏览器,这里的 IEhack 指的是触发 hasLayout。<style>#wrap { border: 1px solid;}#inner { float: left; width: 200px; height: 200px; background: pink;}.clearfix { *zoom: 1; }/ie6 7 不支持伪元素/.clearfix:after { content: ‘’; display: block; clear: both; height: 0; line-height: 0; visibility: hidden; /允许浏览器渲染它,但是不显示出来/}</style><div id=“wrap” class=“clearfix”> <div id=“inner”></div></div>4. 给父元素使用overflow:hidden这种方案让父容器形成了BFC(块级格式上下文),而BFC可以包含浮动,通常用来解决浮动父元素高度坍塌的问题。设置zoom:1清除浮动原理?触发hasLayout,清除浮动。zoom属性是IE浏览器的专有属性,它可以设置或检索对象的缩放比例。解决ie下比较奇葩的bug。譬如外边距(margin)的重叠,浮动清除,触发ie的haslayout属性等。原理:当设置了zoom的值之后,所设置的元素就会就会扩大或者缩小,高度宽度就会重新计算了,这里一旦改变zoom值时其实也会发生重新渲染,运用这个原理,也就解决了ie下子元素浮动时候父元素不随着自动扩大的问题。zoom属是IE浏览器的专有属性,火狐和老版本的webkit核心的浏览器都不支持这个属性。然而,zoom现在已经被逐步标准化,出现在CSS 3.0 规范草案中。目前非ie由于不支持这个属性,它们又是通过什么属性来实现元素的缩放呢? 可以通过css3里面的动画属性scale进行缩放。 ...

February 14, 2019 · 2 min · jiezi

css的content属性

content属性一般用于::before、::after伪元素中,用于呈现伪元素的内容。平时content属性值我们用的最多的就是给个纯字符,其实它还有很多值可供选择。1、插入纯字符<style> *{margin: 0;padding: 0;box-sizing: border-box;} li{list-style: none;} .content{ position: relative;padding: 10px; border: 1px solid #666;margin: 10px; } .content.only-text::before{ content: ‘插入纯字符’; }</style><body> <h1>1、插入纯字符</h1> <div class=“content only-text”></div></body>2、插入图片<style> *{margin: 0;padding: 0;box-sizing: border-box;} li{list-style: none;} .content{ position: relative;padding: 10px; border: 1px solid #666;margin: 10px; } .content.fill-image::before{ content: url(‘https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo_top_86d58ae1.png'); }</style><body> <h1>2、插入图片</h1> <div class=“content fill-image”></div></body>3、插入元素属性<style> *{margin: 0;padding: 0;box-sizing: border-box;} li{list-style: none;} .content{ position: relative;padding: 10px; border: 1px solid #666;margin: 10px; } .content.fill-dom-attr::before{ content: attr(data-title); }</style><body> <h1>3、插入元素属性</h1> <div class=“content fill-dom-attr” data-title=“我是.fill-dom-attr元素的 data-title 属性值”></div></body>4、插入当前元素编号(即当前元素索引)这个特性可用于活动页面的规则介绍。<style> {margin: 0;padding: 0;box-sizing: border-box;} li{list-style: none;} .content{ position: relative;padding: 10px; border: 1px solid #666;margin: 10px; } .fill-dom-index li{ position: relative; / 给计数器加器起个名字,它只会累加 li 标签的索引,li元素中间的div并不会理会 / counter-increment: my; } .fill-dom-index li div::before{ / 使用指定名字的计算器 / content: counter(my)’- ‘; color: #f00; font-weight: 600; }</style><body> <h1>4、插入当前元素编号(即当前元素索引)</h1> <div class=“content fill-dom-index”> <ul> <li><div>我是第1个li标签</div></li> <div>我是li标签中的第1个div标签</div> <li><div>我是第2个li标签</div></li> <li><div>我是第3个li标签</div></li> <div>我是li标签中的第2个div标签</div> <li><div>我是第4个li标签</div></li> <li><div>我是第5个li标签</div></li> </ul> </div></body>5、插入当前元素编号(指定种类)<style> {margin: 0;padding: 0;box-sizing: border-box;} li{list-style: none;} .content{ position: relative;padding: 10px; border: 1px solid #666;margin: 10px; } .fill-dom-index2 li{ position: relative; counter-increment: my2; } .fill-dom-index2 li div::before{ / 第二个参数为list-style-type,可用值见: http://www.w3school.com.cn/cssref/pr_list-style-type.asp/ content: counter(my2,lower-latin)’- ‘; color: #f00; font-weight: 600; }</style><body> <h1>5、插入当前元素编号(指定种类)</h1> <div class=“content fill-dom-index2”> <ul> <li><div>我是第1个li标签</div></li> <div>我是li标签中的第1个div标签</div> <li><div>我是第2个li标签</div></li> <li><div>我是第3个li标签</div></li> <div>我是li标签中的第2个div标签</div> <li><div>我是第4个li标签</div></li> <li><div>我是第5个li标签</div></li> </ul> </div></body> ...

February 13, 2019 · 1 min · jiezi

react中使用css的7中方式(应该是最全的)

第一种: 在组件中直接使用style不需要组件从外部引入css文件,直接在组件中书写。import React, { Component } from “react”;const div1 = { width: “300px”, margin: “30px auto”, backgroundColor: “#44014C”, //驼峰法 minHeight: “200px”, boxSizing: “border-box”};class Test extends Component { constructor(props, context) { super(props); } render() { return ( <div style={div1}>123</div> <div style=“background-color:red;"> ); }}export default Test;注意事项:在正常的css中,比如background-color,box-sizing等属性,在style对象div1中的属性中,必须转换成驼峰法,backgroundColor,boxSizing。而没有连字符的属性,如margin,width等,则在style对象中不变。在正常的css中,css的值不需要用双引好(”"),如.App-header { background-color: #282c34; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: white;}而在react中使用style对象的方式时。值必须用双引号包裹起来。这种方式的react样式,只作用于当前组件。第二种: 在组件中引入[name].css文件需要在当前组件开头使用import引入css文件。import React, { Component } from “react”;import TestChidren from “./TestChidren”;import “@/assets/css/index.scss”;class Test extends Component { constructor(props, context) { super(props); } render() { return ( <div> <div className=“link-name”>123</div> <TestChidren>测试子组件的样式</TestChidren> </div> ); }}export default Test;这种方式引入的css样式,会作用于当前组件及其所有后代组件。第三种: 3、在组件中引入[name].scss文件引入react内部已经支持了后缀为scss的文件,所以只需要安装node-sass即可,因为有个node-sass,scss文件才能在node环境上编译成css文件。>yarn add node-sass然后编写scss文件//index.scss.App{ background-color: #282c34; .header{ min-height: 100vh; color: white; }}关于如何详细的使用sass,请查看sass官网这种方式引入的css样式,同样会作用于当前组件及其所有后代组件。第四种: 在组件中引入[name].module.css文件将css文件作为一个模块引入,这个模块中的所有css,只作用于当前组件。不会影响当前组件的后代组件。import React, { Component } from “react”;import TestChild from “./TestChild”;import moduleCss from “./test.module.css”;class Test extends Component { constructor(props, context) { super(props); } render() { return ( <div> <div className={moduleCss.linkName}>321321</div> <TestChild></TestChild> </div> ); }}export default Test;这种方式可以看做是前面第一种在组件中使用style的升级版。完全将css和组件分离开,又不会影响其他组件。第五种: 在组件中引入 [name].module.scss文件类似于第四种,区别是第四种引入css module,而这种是引入 scss module而已。import React, { Component } from “react”;import TestChild from “./TestChild”;import moduleCss from “./test.module.scss”;class Test extends Component { constructor(props, context) { super(props); } render() { return ( <div> <div className={moduleCss.linkName}>321321</div> <TestChild></TestChild> </div> ); }}export default Test;同样这种方式可以看做是前面第一种在组件中使用style的升级版。第六种: 使用styled-components需要先安装>yarn add styled-components然后创建一个js文件(注意是js文件,不是css文件)//style.jsimport styled, { createGlobalStyle } from “styled-components”;export const SelfLink = styled.div height: 50px; border: 1px solid red; color: yellow;;export const SelfButton = styled.div height: 150px; width: 150px; color: ${props =&gt; props.color}; background-image: url(${props =&gt; props.src}); background-size: 150px 150px;;组件中使用styled-components样式import React, { Component } from “react”;import { SelfLink, SelfButton } from “./style”;class Test extends Component { constructor(props, context) { super(props); } render() { return ( <div> <SelfLink title=“People’s Republic of China”>app.js</SelfLink> <SelfButton color=“palevioletred” style={{ color: “pink” }} src={fist}> SelfButton </SelfButton> </div> ); }}export default Test;这种方式是将整个css样式,和html节点整体合并成一个组件。引入这个组件html和css都有了。它的好处在于可以随时通过往组件上传入 属性,来动态的改变样式。对于处理变量、媒体查询、伪类等较方便的。这种方式的css也只对当前组件有效。具体用法,请查看styled-components官网第七种: 使用radium需要先安装>yarn add radium然后在react组件中直接引入使用import React, { Component } from “react”;import Radium from ‘radium’;let styles = { base: { color: ‘#fff’, ‘:hover’: { background: ‘#0074d9’ } }, primary: { background: ‘#0074D9’ }, warning: { background: ‘#FF4136’ }};class Test extends Component { constructor(props, context) { super(props); } render() { return ( <div> <button style={[ styles.base, styles.primary ]}> this is a primary button </button> </div> ); }}export default Radium(Test); 对于处理变量、媒体查询、伪类等是不方便的。使用Radium可以直接处理变量、媒体查询、伪类等,并且可以直接使用js中的数学,连接,正则表达式,条件,函数等。具体用法请查看radium官网注意:在export之前,必须用Radium包裹。 ...

February 11, 2019 · 2 min · jiezi

创建自己的 CSS 网格系统【转载 | 译】

此文翻译自 Creating Your Own CSS Grid System | Jan,英文不好如有错误 ✖ ,请指正。CSS 网格已经存在很长时间了。它们通常捆绑在 Bootstrap 等框架中。我不是一个 Bootstrap 仇恨者,但如果你真正需要的只是一个网格,有时使用一个框架就“太过分”了。以下是如何从头开始制作自己的 CSS 网格。CSS 网格的元素我们可以看到,基本网格只包含几个元素:Container(容器)row(行)Column(列)Gutter(列之间的空间)容器(Container)容器的目的是设置整个网格的宽度。容器的宽度通常为 100%,但你可能希望设置一个最大宽度。.grid-container { width: 100%; max-width: 1200px;}列之间的空间(gutter)row 元素的目的是使其中的列不会溢出到其他行上。为此,我们将使用 clearfix hack 来确保行内的所有内容都保留在行内。/* 我们的 cleafix hack /.row: before,.row: after { content: “”; display: table; clear: both;}列(Column)column 是网格中最复杂的部分。首先,有几种不同的方法在 CSS 中定位 column,然后有各种宽度要考虑,以及响应式设计等因素。在本教程中,我们将定义 column 并赋予它们宽度。列定位(Column Positioning)float, inline-block, display: table, display: flex。这些都是在 CSS 中定位 column 的不同方法。这些方法中最容易出错和最广泛使用的是“浮动”方法。如果我们的列是空的,那么我们的浮动列将堆叠在一起。为了防止这种情况,给 column 提供 1px 的最小高度并使它们浮动。[class=‘col-’] { float: left; min-height: 1px; }列宽(Column Widths)要查找一列的宽度,我们所要做的就是将总列数除以容器的宽度。在我们的例子中,容器的宽度是 100%,我们想要6 列,所以 100/6 = 16.66,所以我们的基本列宽度是 16.66%。[class*=‘col-’] { float: left; min-height: 1px; width: 16.66%; }这当然只是一个开始。如果我们想要一个 2 列宽的部分,我们必须创建一个 2 列宽的 column。计算非常简单。我们在使用这些列组合时唯一考虑的是,一行中的总列数最多为 6(或者总列数是多少)。响应式系统中内边距问题在 W3C 标准盒模型条件下,在响应式系统中给宽度单位为百分比的元素设置内边距很麻烦。幸运的是,使用 border-box 模型,我们可以轻松设置内边距。 /* 在网格内的所有元素上改变盒模型 /.grid-container { box-sizing: border-box; }[class=‘col-’] { float: left; min-height: 1px; width: 16.66%; / 设置内边距 / padding: 12px;}使用 * {box-sizing: border-box;} 在 CSS 中改变所有元素的盒模型。基本网格准备好了<div class=“grid-container outline”> <div class=“row”> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> </div> <div class=“row”> <div class=“col-2”><p>col-2</p></div> <div class=“col-2”><p>col-2</p></div> <div class=“col-2”><p>col-2</p></div> </div> <div class=“row”> <div class=“col-3”><p>col-3</p></div> <div class=“col-3”><p>col-3</p></div> </div> </div>CSS.grid-container{ width: 100%; max-width: 1200px;}/ cleafix hack /.row:before,.row:after { content:""; display: table ; clear:both;}[class=‘col-’] { float: left; min-height: 1px; width: 16.66%; padding: 12px; background-color: #FFDCDC;}.col-1{ width: 16.66%; }.col-2{ width: 33.33%; }.col-3{ width: 50%; }.col-4{ width: 66.66%; }.col-5{ width: 83.33%; }.col-6{ width: 100%; }.outline, .outline { outline: 1px solid #F6A1A1;}/ 一些额外的列内容样式 /[class=‘col-’] > p { background-color: #FFC2C2; padding: 0; margin: 0; text-align: center; color: white;}HTML<div class=“grid-container outline”> <div class=“row”> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> </div> <div class=“row”> <div class=“col-2”><p>col-2</p></div> <div class=“col-2”><p>col-2</p></div> <div class=“col-2”><p>col-2</p></div> </div> <div class=“row”> <div class=“col-3”><p>col-3</p></div> <div class=“col-3”><p>col-3</p></div> </div></div>使我们的网格系统响应调整我们的网格以实现移动布局非常简单。我们所要做的就是调整列的宽度。为了简单起见,我将为 800px 以下的屏幕加倍列宽。唯一需要注意的是一些例外,例如:大于 col-3 的列在视口小于 800px 时铺满。@media all and (max-width:800px){ .col-1{ width: 33.33%; } .col-2{ width: 50%; } .col-3{ width: 83.33%; } .col-4{ width: 100%; } .col-5{ width: 100%; } .col-6{ width: 100%; } .row .col-2:last-of-type{ width: 100%; } .row .col-5 ~ .col-1{ width: 100%; }}对于小于 650px 的屏幕。@media all and (max-width:650px){ .col-1{ width: 50%; } .col-2{ width: 100%; } .col-3{ width: 100%; } .col-4{ width: 100%; } .col-5{ width: 100%; } .col-6{ width: 100%; }}我们现在创建了自己的响应式网格系统,而不使用框架。<div class=“grid-container outline”> <div class=“row”> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> <div class=“col-1”><p>col-1</p></div> </div> <div class=“row”> <div class=“col-2”><p>col-2</p></div> <div class=“col-2”><p>col-2</p></div> <div class=“col-2”><p>col-2</p></div> </div> <div class=“row”> <div class=“col-3”><p>col-3</p></div> <div class=“col-3”><p>col-3</p></div> </div> <div class=“row”> <div class=“col-4”><p>col-4</p></div> <div class=“col-2”><p>col-2</p></div> </div> <div class=“row”> <div class=“col-5”><p>col-5</p></div> <div class=“col-1”><p>col-1</p></div> </div> <div class=“row”> <div class=“col-6”><p>col-6</p></div> </div></div>注意:本指南只是创建自己响应式网格系统的起点。它不是一个框架,甚至不是一个完整的解决方案,我希望它能够揭开 CSS 网格的神秘面纱。参考【1】Creating Your Own CSS Grid System | Jan ...

January 27, 2019 · 2 min · jiezi

三分钟掌握 React render props

上午review 代码的时候, 发现一些问题, 关于逻辑复用的一些小技巧。 然后就花点时间整理了下, 做了个例子, 聊一下 render props.what is it ?简单点讲, render props 就一种在组件间共享逻辑的技巧。 把一些渲染逻辑以prop 的形式传递给 Component, 把注意力集中在渲染逻辑上。What do render props do?处理组件的渲染逻辑。When to use ?当你发现组件中有重复的逻辑或者模式的时候。比如:重复的UI结构共享某个数据源共享某个全局事件(比如scroll, resize, …)A live demo光说不练假把式, 一起看个例子。想了想, 写个表吧, 会动的那种(年会毛都没中,给个手环也好啊..)。一番操作之后, 我们花了一个表:现在我们又想换个表带, 改怎么做? 重写一个吗? 显然不是。这时候就轮到 render props 登场了。我们可以把一个个部分独立出来, 把有差异的部分当作prop 传入就可以了。上代码:class Watch extends Component { state = { date: moment(), } static propTypes = { face: PropTypes.func, } static defaultProps = { face: date => <DefaultFace date={date} />, } componentDidMount = () => (this.TICK = setInterval(this.update, 1000)) componentWillUnmount = () => clearInterval(this.TICK) update = () => this.setState({ date: moment() }) render = () => ( <Strap> <Bezel> <Screen> <Face>{this.props.face(this.state.date)}</Face> </Screen> </Bezel> </Strap> )}不用关注 Strap, Bezel, Screen 这些, 我们只看关键点: Face.如果我们啥也不传, 得到的将是一个空空如也的表盘:这时候可以给他加个 DefaultFace, 显示 HH:mm static defaultProps = { face: date => <DefaultFace date={date} />, }很赞。现在给他换个样子, 骚黄色:const SecondsFace = ({ date }) => { const hours = date.format(‘HH’) const minutes = date.format(‘mm’) const seconds = date.format(‘ss’) return ( <> <Value>{hours}</Value> <Value>{minutes}</Value> <Value>{seconds}</Value> </> )}SecondsFace.propTypes = watchfacePropTypes;心满意足。这时候我们的render 是这样的:class App extends Component { render() { return ( <Watch /> <Watch face={date => <SecondsFace date={date} />} /> </div> ); }}如此这般, 可以把其他款式的表都写了:舒服~年会又又又又啥也没中的心情放佛得到了安抚。完整代码在这里: 代码 ,喜欢的可以给个星星。Live Demo : codeOpenTipsDos ????当有组件可以共享或者部分渲染逻辑重复的时候Dont’s ????宁可不用也不要滥用避免在使用PureComponents 的时候用render Props. 除非你的prop 是静态定义的。Notes ⚠️Render Prop 只是一个技巧,名字啥都可以, 比如 children。大部分可以用Render Prop的场景, 也可以用HOC 实现, 反之亦然。That’s it.:)如有纰漏, 欢迎指正,谢谢。更多参考:https://reactjs.org/docs/rend… ...

January 24, 2019 · 1 min · jiezi

伪类与伪元素

一、伪类伪类包含两种:状态伪类(UI 伪类)和结构性伪类。(1)状态伪类是基于元素当前状态进行选择的。在与用户的交互过程中元素的状态是动态变化的,因此该元素会根据其状态呈现不同的样式。当元素处于某状态时会呈现该样式,而进入另一状态后,该样式也会失去。常见的状态伪类主要包括::link 应用于未被访问过的链接; :hover 应用于鼠标悬停到的元素; :active 应用于被激活的元素; :visited 应用于被访问过的链接,与:link互斥。 :focus 应用于拥有键盘输入焦点的元素。 :target 应用于链接点击后指向元素前 4 个伪类的特指度相同,如果不按照这里列出的顺序使用它们,浏览器可能不会显示预期结果。为了好记,可以这么想:“LoVe? HA!”大写字母就是每个伪类的头一个字母。input:focus {border:1px solid blue;} 会在光标位于 input 字段中时,为该字段添加一个蓝色边框。这样可以让用户明确地知道输入的字符会出现在哪里。<a href="#more_info">More Information</a><h2 id=“more_info”>This is the information you are looking for.</h2> #more_info:target {background:#eee;} 会在用户单击链接转向 ID 为 more_info的元素时,为该元素添加浅灰色背景。(2)结构性伪类是css3新增选择器利用dom树进行元素过滤,通过文档结构的互相关系来匹配元素,能够减少class和id属性的定义,使文档结构更简洁。常见的包括::first-child 选择某个元素的第一个子元素; :last-child 选择某个元素的最后一个子元素; :nth-child() 选择某个元素的一个或多个特定的子元素; :nth-last-child() 选择某个元素的一个或多个特定的子元素,从这个元素的最后一个子元素开始算; :nth-of-type() 选择指定的元素; :nth-last-of-type() 选择指定的元素,从元素的最后一个开始计算; :first-of-type 选择一个上级元素下的第一个同类子元素; :last-of-type 选择一个上级元素的最后一个同类子元素; :only-child 选择的元素是它的父元素的唯一一个子元素; :only-of-type 选择一个元素是它的上级元素的唯一一个相同类型的子元素; :empty 选择的元素里面没有任何内容。二、伪元素伪元素是对元素中的特定内容进行操作,而不是描述状态。它的操作层次比伪类更深一层,因此动态性比伪类低很多。实际上,伪元素就是选取某些元素前面或后面这种普通选择器无法完成的工作。控制的内容和元素是相同的,但它本身是基于元素的抽象,并不存在于文档结构中!常见的伪元素选择器包括::first-letter 选择元素文本的第一个字(母)。 :first-line 选择元素文本的第一行。 :before 在元素内容的最前面添加新内容。 :after 在元素内容的最后面添加新内容。三、注意单冒号(:)用于 CSS3 伪类,双冒号(::)用于 CSS3 伪元素,为了兼容某些浏览器,一般都采用单冒号兼容性的问题,交给postcss去做。本文并未涉及兼容性的写法,包括前缀问题,可以交给autoprefixer去做。(这句话啥意思没懂,以后看看说的啥)伪类(结构伪类)的效果可以通过添加一个实际的类来达到,而伪元素的效果则需要通过添加一个实际的元素才能达到,这也是为什么他们一个称为伪类,一个称为伪元素的原因。四、伪元素的使用(1) 清除浮动.clear:after {content: ‘’;display: block;clear: both;}(2) 画分割线<style>* { padding: 0; margin: 0;}.spliter::before, .spliter::after { content: ‘’; display: inline-block; border-top: 1px solid black; width: 200px; margin: 5px;} </style></head><body> <p class=“spliter”>分割线</p></body>五、参考文献 https://segmentfault.com/a/1190000000484493 详解 CSS 属性 - 伪类和伪元素的区别 https://segmentfault.com/a/1190000012156828 谈谈css伪类与伪元素《css设计指南》 ...

January 23, 2019 · 1 min · jiezi

Animate.css 超强CSS3动画库,三行代码搞定H5页面动画特效!

一、基本用法引入CSS依赖<link rel=“stylesheet” href=“https://cdn.bootcss.com/animate.css/3.7.0/animate.min.css">在元素的Class中加以下内容animated (必选)infinite (可选) 无限重复bounce (必选) 动画样式 参考下方表格delay-2s (可选) 延迟出现 <div class=“animated infinite bounce delay-2s”><h1>Example</h1></div>Class Name bounceflashpulserubberBandshakeheadShakeswingtadawobblejellobounceInbounceInDownbounceInLeftbounceInRightbounceInUpbounceOutbounceOutDownbounceOutLeftbounceOutRightbounceOutUpfadeInfadeInDownfadeInDownBigfadeInLeftfadeInLeftBigfadeInRightfadeInRightBigfadeInUpfadeInUpBigfadeOutfadeOutDownfadeOutDownBigfadeOutLeftfadeOutLeftBigfadeOutRightfadeOutRightBigfadeOutUpfadeOutUpBigflipInXflipInYflipOutXflipOutYlightSpeedInlightSpeedOutrotateInrotateInDownLeftrotateInDownRightrotateInUpLeftrotateInUpRightrotateOutrotateOutDownLeftrotateOutDownRightrotateOutUpLeftrotateOutUpRighthingejackInTheBoxrollInrollOutzoomInzoomInDownzoomInLeftzoomInRightzoomInUpzoomOutzoomOutDownzoomOutLeftzoomOutRightzoomOutUpslideInDownslideInLeftslideInRightslideInUpslideOutDownslideOutLeftslideOutRightslideOutUpheartBeat大功告成,刷新页面即可查看动画效果。基本用法就是这些官方还给出了一些进阶用法如下二、进阶用法动态调用动画的Javascript例子function animateCss(element, animationName, callback) { const node = document.querySelector(element) node.classList.add(‘animated’, animationName) function handleAnimationEnd() { node.classList.remove(‘animated’, animationName) node.removeEventListener(‘animationend’, handleAnimationEnd) if (typeof callback === ‘function’) callback() } node.addEventListener(‘animationend’, handleAnimationEnd)}三、在官方例子基础上,稍加修改以后由于官方例子用的是querySelector,故只会选中第一个符合要求的元素。并且持续时间只有slow(2s)、slower(3s)、fast(800ms)、faster(500ms)故我稍加修改,依然用的原生JS语法(部分ES6)其中选择器element改为选中所有符合要求的元素新增times参数,可以是2000ms或者2s/** * element: 选择器 例如 #id | .class | div * animationName: 动画名称 参考animate.css官网 例如fadeIn * times: 持续时间 例如 200ms | 2s * callback: 回调函数 */function animateCss(element, animationName,times, callback) { const nodes = document.querySelectorAll(element) nodes.forEach((node => { if(times) node.style.setProperty(‘animation-duration’, times, ‘important’); node.classList.add(‘animated’, animationName) function handleAnimationEnd() { node.classList.remove(‘animated’, animationName) node.removeEventListener(‘animationend’, handleAnimationEnd) if (typeof callback === ‘function’) callback() } node.addEventListener(‘animationend’, handleAnimationEnd) }))}例子animateCss(’.post’, ‘pulse’);animateCss(’.post’, ‘pulse’,‘200ms’);animateCss(’.post’, ‘pulse’,‘200ms’,function(){//do something});Animate.css官网https://daneden.github.io/animate.css/https://github.com/daneden/animate.css另外本篇文章也发表在了我的个人主页,欢迎来查看https://zzzmh.cn/single?id=59 ...

January 23, 2019 · 1 min · jiezi

一步步教你用HTML5 SVG实现动画效果

翻译:疯狂的技术宅原文:https://www.smashingmagazine….本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章摘要在这篇文章中你将了解Awwwards网是怎样实现动画的。 本文介绍了HTML5 SVG中的circle 元素,它的stroke属性,以及如何使用CSS变量以及用 Vanilla JavaScript 为它们设置动画。SVG是一种基于XML的,用于定义缩放矢量图形的标记语言。 它允许你通过在2D平面中确定的一组点来绘制路径、曲线和形状。 此外你还可以通过在这些路径上添加动态属性(例如笔触,颜色,粗细,填充等)来生成动画。从2017年4月起,CSS Level 3 填充和描边模块开始支持从外部样式表设置SVG颜色和填充图案,而不是在每个元素上设置属性。 在本教程中,我们将会使用简单的纯十六进制颜色,不过填充和描边属性也支持图案,渐变和图像作为值。注意:访问Awwwards网站时,你需要把浏览器宽度设置为1024px或更高的才能更好的查看动画显示。演示链接源代码文件结构让我们从在终端中创建文件开始:???? mkdir note-display???? cd note-display???? touch index.html styles.css scripts.jsHTML这是连接css和js文件的初始模板:<html lang=“en”><head> <meta charset=“UTF-8”> <title>Note Display</title> <link rel=“stylesheet” href="./styles.css"></head><body> <script src="./scripts.js"></script></body></html>每个note元素都包含一个列表项:li用于保存circle,note值及其label。图:列出项元素及其直接子元素:.circle, .percent 和 .label.circle_svg是一个SVG元素,它包含两个 <circle>元素。 第一个是要填充的路径,第二个用来为动画作准备。图:SVG元素:SVG包装器和圆形标签注释分为整数和小数,所以可以把它们设定为不同大小的字体。 label 是一个简单的<span>。 把所有得这些元素放在一起看起来像这样:<li class=“note-display”> <div class=“circle”> <svg width=“84” height=“84” class=“circle__svg”> <circle cx=“41” cy=“41” r=“38” class=“circle__progress circle__progress–path”></circle> <circle cx=“41” cy=“41” r=“38” class=“circle__progress circle__progress–fill”></circle> </svg> <div class=“percent”> <span class=“percent__int”>0.</span> <span class=“percent__dec”>00</span> </div> </div> <span class=“label”>Transparent</span></li>cx和cy属性定义圆的x轴和y轴中心点。 r属性定义其半径。你可能已经注意到类名中的下划线/破折号模式。 这是BEM(block element modifier),分别代表 block, element 和 modifier。 它是使元素命名更加结构化、有条理和语义化的一种方法。推荐阅读:什么是BEM以及为什么需要它为了完成模板结构,让我们将四个列表项包装在无序列表元素中:图:无序列表包装器拥有四个li子元素<ul class=“display-container”> <li class=“note-display”> <div class=“circle”> <svg width=“84” height=“84” class=“circle__svg”> <circle cx=“41” cy=“41” r=“38” class=“circle__progress circle__progress–path”></circle> <circle cx=“41” cy=“41” r=“38” class=“circle__progress circle__progress–fill”></circle> </svg> <div class=“percent”> <span class=“percent__int”>0.</span> <span class=“percent__dec”>00</span> </div> </div> <span class=“label”>Transparent</span> </li> <li class=“note-display”> <div class=“circle”> <svg width=“84” height=“84” class=“circle__svg”> <circle cx=“41” cy=“41” r=“38” class=“circle__progress circle__progress–path”></circle> <circle cx=“41” cy=“41” r=“38” class=“circle__progress circle__progress–fill”></circle> </svg> <div class=“percent”> <span class=“percent__int”>0.</span> <span class=“percent__dec”>00</span> </div> </div> <span class=“label”>Reasonable</span> </li> <li class=“note-display”> <div class=“circle”> <svg width=“84” height=“84” class=“circle__svg”> <circle cx=“41” cy=“41” r=“38” class=“circle__progress circle__progress–path”></circle> <circle cx=“41” cy=“41” r=“38” class=“circle__progress circle__progress–fill”></circle> </svg> <div class=“percent”> <span class=“percent__int”>0.</span> <span class=“percent__dec”>00</span> </div> </div> <span class=“label”>Usable</span> </li> <li class=“note-display”> <div class=“circle”> <svg width=“84” height=“84” class=“circle__svg”> <circle cx=“41” cy=“41” r=“38” class=“circle__progress circle__progress–path”></circle> <circle cx=“41” cy=“41” r=“38” class=“circle__progress circle__progress–fill”></circle> </svg> <div class=“percent”> <span class=“percent__int”>0.</span> <span class=“percent__dec”>00</span> </div> </div> <span class=“label”>Exemplary</span> </li></ul>你必须先问一下自己 Transparent、 Reasonable、 Usable 和 Exemplary 标签都代表什么意思。 随着你对编程的不断熟悉,就会发现写代码不仅仅是为了能够使程序正常运行,还需要要确保它能够被长期维护和扩展。 这些只有在你的代码容易被修改时才能够实现。“缩略词TRUE应该能够帮助你确定自己编写的代码是否能够适应未来的变化。”那么,下次问问你自己:透明:代码更改后果是否明确?合理:成本效益值得吗?可用:我是否能够在意外情况下重复使用它?示例:它是否以高质量作为未来代码的示例?Transparent(透明):代码在修改后果是否明确?Reasonable(合理):成本效益值得吗?Usable(可用):我是否能够在不同的场景下重复使用它?Exemplary(示例):未来它是否可以作为高质量作为代码范本?注:Sandi Metz在《面向对象设计实践指南:Ruby语言描述》一书解释了TRUE和其他原则,以及如何通过设计模式实现它们。 如果你还没有开始研究设计模式,请考虑将此书放到自己的案头。CSS让我们导入字体并使其对所有内容生效:@import url(‘https://fonts.googleapis.com/css?family=Nixie+One|Raleway:200’);* { padding: 0; margin: 0; box-sizing: border-box;}box-sizing: border-box 属性中包括填充与边框值到元素的总宽度和高度,所以更容易计算图形的范围。注意:有关 box-sizing的说明,请阅读“使用CSS Box让你更轻松”_。body { height: 100vh; color: #fff; display: flex; background: #3E423A; font-family: ‘Nixie One’, cursive;}.display-container { margin: auto; display: flex;}通过组合规则显示:body 中的 flex 和 .display-container 中的 margin-auto,可以将子元素垂直水平居中。 .display-container元素也将作为一个 flex-container; 这样,它的子元素会沿主轴被放置在同一行。.note-display 列表项也将是一个 flex-container。 由于有很多子项被居中,所以我们可以通过 justify-content 和 align-items 属性来完成。 所有 flex-items 都将垂直水平居中。 如果你不确定它们是什么,请查看“CSS Flexbox 可视化指南”中的对齐部分。.note-display { display: flex; flex-direction: column; align-items: center; margin: 0 25px;}让我们通过设置stroke-width,stroke-opacity 和 stroke-linecap 将笔划应用于圆,这些规则会使画面动起来。 接下来,我们为每个圆添加一种颜色:.circle__progress { fill: none; stroke-width: 3; stroke-opacity: 0.3; stroke-linecap: round;}.note-display:nth-child(1) .circle__progress { stroke: #AAFF00; }.note-display:nth-child(2) .circle__progress { stroke: #FF00AA; }.note-display:nth-child(3) .circle__progress { stroke: #AA00FF; }.note-display:nth-child(4) .circle__progress { stroke: #00AAFF; }为了绝对定位百分比元素,必须完全知道这些概念是什么。 .circle元素应该是引用,所以让我们为其添加添加 position: relative 。注意:对绝对定位更深入、直观的解释,请阅读“一劳永逸的理解 CSS Position”一文。另一种使元素居中的方法是把 top: 50%, left: 50% 和 transform: translate(-50%, -50%); 组合在一起, 将元素的中心定位在其父级中心。.circle { position: relative;}.percent { width: 100%; top: 50%; left: 50%; position: absolute; font-weight: bold; text-align: center; line-height: 28px; transform: translate(-50%, -50%);}.percent__int { font-size: 28px; }.percent__dec { font-size: 12px; }.label { font-family: 'Raleway', serif; font-size: 14px; text-transform: uppercase; margin-top: 15px;}到目前为止,模板应如该是下面这个样子:图:完成的模板元素和样式填充过渡可以在两个圆形SVG属性的帮助下创建圆形动画:stroke-dasharray 和 stroke-dashoffset。“stroke-dasharray 定义笔划中的虚线间隙模式。”它最多可能需要四个值:当它被设置为唯一的整数( stroke-dasharray:10 )时,破折号和间隙具有相同的大小;对于两个值( stroke-dasharray:10 5 ),第一个应用于破折号,第二个应用于间隙;第三种和第四种形式(stroke-dasharray:10 5 2 和 stroke-dasharray:10 5 2 3 )将产生各种样式的虚线和间隙。图:stroke-dasharray属性值左边的图像显示属性stroke-dasharray设置为 0 到圆周长度 238px。第二个图像表示 stroke-dashoffset 属性,它抵消了dash数组的开头。 它的取值范围也是从0到圆周长度。图:stroke-dasharray 和 stroke-dashoffset 属性为了产生填充效果,我们将 stroke-dasharray 设置为圆周长度,以便它所有长度都能充满其冲刺范围而不留间隙。 我们也会用相同的值抵消它,这样会使它能够被“隐藏”。 然后,stroke-dashoffset 将更新为对应的说明文字,根据过渡持续时间填充其行程。属性更新将通过CSS Variables在脚本中完成。 下面让我们声明变量并设置属性:.circle__progress--fill { --initialStroke: 0; --transitionDuration: 0; stroke-opacity: 1; stroke-dasharray: var(--initialStroke); stroke-dashoffset: var(--initialStroke); transition: stroke-dashoffset var(--transitionDuration) ease;}为了设置初始值并更新变量,让我们从使用 document.querySelectorAll 选择所有.note-display元素开始。 同时把 transitionDuration设置为900毫秒。然后,我们遍历显示数组,选择它的 .circle__progress.circle__progress--fill 并提取HTML中的 r 属性集来计算周长。 有了它,我们可以设置初始的 --dasharray 和 --dashoffset 值。当 --dashoffset 变量被 setTimeout 更新时,将发生动画:const displays = document.querySelectorAll('.note-display');const transitionDuration = 900;displays.forEach(display =&gt; { let progress = display.querySelector('.circle__progress--fill'); let radius = progress.r.baseVal.value; let circumference = 2 * Math.PI * radius; progress.style.setProperty('--transitionDuration', ${transitionDuration}ms); progress.style.setProperty('--initialStroke', circumference); setTimeout(() =&gt; progress.style.strokeDashoffset = 50, 100);});要从顶部开始过度,必须旋转 .circle__svg 元素:.circle__svg { transform: rotate(-90deg);}图:Stroke 属性转换现在,让我们计算相对于 note 的dashoffset值。 note 值将通过 data-* 属性插入每个li项目。 * 可以替换为任何符合你需求的名称,然后可以通过元素的数据集在元数据集中检索:element.dataset.*。注意:你可以在MDN Web Docs上得到有关 data-* 属性的更多信息。我们的属性将被命名为 “data-note”:&lt;ul class="display-container"&gt;+ &lt;li class="note-display" data-note="7.50"&gt; &lt;div class="circle"&gt; &lt;svg width="84" height="84" class="circle__svg"&gt; &lt;circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"&gt;&lt;/circle&gt; &lt;circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"&gt;&lt;/circle&gt; &lt;/svg&gt; &lt;div class="percent"&gt; &lt;span class="percent__int"&gt;0.&lt;/span&gt; &lt;span class="percent__dec"&gt;00&lt;/span&gt; &lt;/div&gt; &lt;/div&gt; &lt;span class="label"&gt;Transparent&lt;/span&gt; &lt;/li&gt;+ &lt;li class="note-display" data-note="9.27"&gt; &lt;div class="circle"&gt; &lt;svg width="84" height="84" class="circle__svg"&gt; &lt;circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"&gt;&lt;/circle&gt; &lt;circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"&gt;&lt;/circle&gt; &lt;/svg&gt; &lt;div class="percent"&gt; &lt;span class="percent__int"&gt;0.&lt;/span&gt; &lt;span class="percent__dec"&gt;00&lt;/span&gt; &lt;/div&gt; &lt;/div&gt; &lt;span class="label"&gt;Reasonable&lt;/span&gt; &lt;/li&gt;+ &lt;li class="note-display" data-note="6.93"&gt; &lt;div class="circle"&gt; &lt;svg width="84" height="84" class="circle__svg"&gt; &lt;circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"&gt;&lt;/circle&gt; &lt;circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"&gt;&lt;/circle&gt; &lt;/svg&gt; &lt;div class="percent"&gt; &lt;span class="percent__int"&gt;0.&lt;/span&gt; &lt;span class="percent__dec"&gt;00&lt;/span&gt; &lt;/div&gt; &lt;/div&gt; &lt;span class="label"&gt;Usable&lt;/span&gt; &lt;/li&gt;+ &lt;li class="note-display" data-note="8.72"&gt; &lt;div class="circle"&gt; &lt;svg width="84" height="84" class="circle__svg"&gt; &lt;circle cx="41" cy="41" r="38" class="circle__progress circle__progress--path"&gt;&lt;/circle&gt; &lt;circle cx="41" cy="41" r="38" class="circle__progress circle__progress--fill"&gt;&lt;/circle&gt; &lt;/svg&gt; &lt;div class="percent"&gt; &lt;span class="percent__int"&gt;0.&lt;/span&gt; &lt;span class="percent__dec"&gt;00&lt;/span&gt; &lt;/div&gt; &lt;/div&gt; &lt;span class="label"&gt;Exemplary&lt;/span&gt; &lt;/li&gt;&lt;/ul&gt;parseFloat方法将display.dataset.note返回的字符串转换为浮点数。 offset 表示达到最高值时缺失的百分比。 因此,对于 7.50 note,我们将得到 (10 - 7.50) / 10 = 0.25,这意味着 circumference 长度应该偏移其值的25%:let note = parseFloat(display.dataset.note);let offset = circumference * (10 - note) / 10;更新scripts.js:const displays = document.querySelectorAll('.note-display');const transitionDuration = 900;displays.forEach(display =&gt; { let progress = display.querySelector('.circle__progress--fill'); let radius = progress.r.baseVal.value; let circumference = 2 * Math.PI * radius;+ let note = parseFloat(display.dataset.note);+ let offset = circumference * (10 - note) / 10; progress.style.setProperty('--initialStroke', circumference); progress.style.setProperty('--transitionDuration', ${transitionDuration}ms);+ setTimeout(() =&gt; progress.style.strokeDashoffset = offset, 100);});sroke属性转换为note值在继续之前,让我们将stoke转换提取到它自己的方法中:const displays = document.querySelectorAll('.note-display');const transitionDuration = 900;displays.forEach(display =&gt; {- let progress = display.querySelector('.circle__progress--fill');- let radius = progress.r.baseVal.value;- let circumference = 2 * Math.PI * radius; let note = parseFloat(display.dataset.note);- let offset = circumference * (10 - note) / 10;- progress.style.setProperty('--initialStroke', circumference);- progress.style.setProperty('--transitionDuration', ${transitionDuration}ms);- setTimeout(() =&gt; progress.style.strokeDashoffset = offset, 100);+ strokeTransition(display, note);});+ function strokeTransition(display, note) {+ let progress = display.querySelector('.circle__progress--fill');+ let radius = progress.r.baseVal.value;+ let circumference = 2 * Math.PI * radius;+ let offset = circumference * (10 - note) / 10;+ progress.style.setProperty('--initialStroke', circumference);+ progress.style.setProperty('--transitionDuration', ${transitionDuration}ms);+ setTimeout(() =&gt; progress.style.strokeDashoffset = offset, 100);+ }注意增长值还有一件事就是把 note 从0.00转换到要最终的 note 值。 首先要做的是分隔整数和小数值。 可以使用字符串方法split()。 之后它们将被转换为数字,并作为参数传递给 increaseNumber() 函数,通过整数和小数的标志正确显示在对应元素上。const displays = document.querySelectorAll('.note-display');const transitionDuration = 900;displays.forEach(display =&gt; { let note = parseFloat(display.dataset.note);+ let [int, dec] = display.dataset.note.split('.');+ [int, dec] = [Number(int), Number(dec)]; strokeTransition(display, note);+ increaseNumber(display, int, 'int');+ increaseNumber(display, dec, 'dec');});在 increaseNumber() 函数中,我们究竟选择 .percent__int 还是 .percent__dec 元素,取决于 className ,以及输出是否应包含小数点。 接下来把transitionDuration设置为900毫秒。 现在,动画表示从0到7的数字,持续时间必须除以note 900 / 7 = 128.57ms。 结果表示每次增加迭代将花费多长时间。 这意味着 setInterval将每隔 128.57ms 触发一次。设置好这些变量后,接着定义setInterval。 counter 变量将作为文本附加到元素,并在每次迭代时增加:function increaseNumber(display, number, className) { let element = display.querySelector(.percent__${className}), decPoint = className === 'int' ? '.' : '', interval = transitionDuration / number, counter = 0; let increaseInterval = setInterval(() =&gt; { element.textContent = counter + decPoint; counter++; }, interval);}图:计数增长太酷了! 确实增加了计数值,但它在无限循环播放。 当note达到我们想要的值时,还需要清除setInterval。 可以通过clearInterval函数完成:function increaseNumber(display, number, className) { let element = display.querySelector(.percent__${className}`), decPoint = className === ‘int’ ? ‘.’ : ‘’, interval = transitionDuration / number, counter = 0; let increaseInterval = setInterval(() => {+ if (counter === number) { window.clearInterval(increaseInterval); } element.textContent = counter + decPoint; counter++; }, interval);}图:最终完成现在,数字更新到note值,并使用clearInterval()函数清除。教程到此就结束了,希望你能喜欢它!如果你想开发一些更具互动性的东西,请查看使用 Vanilla JavaScript 创建的Memory Game Tutorial 。 它涵盖了基本的HTML5,CSS3和JavaScript概念,如定位、透视、转换、Flexbox、事件处理、超时和三元组。祝你快乐的编码!????本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章 ...

January 22, 2019 · 5 min · jiezi

【收藏】2019年最新Vue相关精品开源项目库汇总

前言本文的前身是源自github上的项目awesome-github-vue,但由于该项目上次更新时间为2017年6月12日,很多内容早已过期或是很多近期优秀组件未被收录,所以小肆今天(2019/01/19)重新更新了内容并新建项目awesome-vue。针对原项目小肆做了如下几点更新更新全部项目github上的star数根据最新star数进行排序去掉所有star数不足1000的项目去掉所有一年内未更新的项目增加部分新项目日后维护计划每半月更新一次做更明确详细的分类添加优秀学习资源欢迎到Issues提交项目给小肆,我会第一时间处理。提交的项目格式如下:项目名称:XXXXX子标题: XXXXX相关介绍: XXXXXX如果收录的项目有错误,可以通过Issues反馈给小肆。star数未满1000的项目,也可以提交,小肆会保持关注,如果能解决用户痛点且没有更优秀的项目替代,小肆会提前更新至文档欢迎关注小肆公众号:技术放肆聊目录UI组件开发框架实用库服务端应用实例Demo示例UI组件element ★34436 - 饿了么出品的Vue2的web UI工具套件iview ★19488 - 基于 Vuejs 的开源 UI 组件库vuetify ★16076 - 为移动而生的Vue JS 2组件框架Vux ★14975 - 基于Vue和WeUI的组件库mint-ui ★13302 - Vue 2的移动UI元素bootstrap-vue ★7646 - 应用于Vuejs2的Twitter的Bootstrap 4组件vue-material ★7510 - 通过Vue Material和Vue 2建立精美的app应用vant ★7246 - 有赞出品的Vue2.0移动UImuse-ui ★7047 - 三端样式一致的响应式 UI 库Vue.Draggable ★6329 - 实现拖放和视图模型数组同步vue-awesome-swiper ★5717 - vue.js触摸滑动组件vueAdmin ★4792 - 基于vuejs2和element的简单的管理员模板buefy ★4442 - 响应式UI组件轻量级库vue-multiselect ★3849 - Vue.js选择框解决方案Keen-UI ★3604 - 轻量级的基本UI组件合集vue-quill-editor ★3493 - 基于Quill适用于Vue2的富文本编辑器eagle.js ★3320 - hacker的幻灯片演示框架vonic ★2997 - 快速构建移动端单页应用vue-echarts ★2751 - VueJS的ECharts组件vuesax ★2674 - 灵动的小组件库vue-chartjs ★2631 - vue中的Chartjs的封装vue-ydui ★2282 - 基于Vue2的移动端和微信UIvue-js-modal ★2075 - 移动友好的Vuejs2的modalmavonEditor ★2066 - 基于Vue的markdown编辑器vue-infinite-scroll ★1882 - VueJS的无限滚动指令vue-beauty ★1868 - 由vue和ant design创建的优美UI组件vue-amap ★1860 - 基于Vue 2和高德地图的地图组件eme ★1769 - 优雅的Markdown编辑器vue-video-player ★1706 - VueJS视频及直播播放器vue-table ★1690 - 简化数据表格vuejs-datepicker ★1643 - vue日期选择器组件vue-virtual-scroller ★1643 - 带任意数目数据的顺畅的滚动vue-infinite-loading ★1508 - VueJS的无限滚动插件vue-blu ★1444 - 帮助你轻松创建web应用vue-scroller ★1442 - Vonic UI的功能性组件vue-waterfall ★1431 - Vue.js的瀑布布局组件vue-upload-component ★1372 - Vuejs文件上传组件vue-recyclerview ★1286 - 管理大列表的vue-recyclerviewvue2-editor ★1282 - HTML编辑器vue-chat ★1249 - vuejs和vuex及webpack的聊天示例vue-cropper ★1192 - 一个简单的vue 的图片裁剪插件vue-slider-component ★1149 - 在vue1和vue2中使用滑块vue-dropzone ★1139 - 用于文件上传的Vue组件vue-core-image-upload ★1124 - 轻量级的vue上传插件vue-syntax-highlight ★1198 - Sublime Text语法高亮vue-image-crop-upload ★1108 - vue图片剪裁上传组件vue-baidu-map ★1078 - 基于 Vue 2的百度地图组件库VueCircleMenu ★1072 - 漂亮的vue圆环菜单vue-calendar ★1070 - 日期选择插件vue-tables-2 ★1048 - 显示数据的bootstrap样式网格开发框架vue.js ★125518 - 流行的轻量高效的前端组件化方案vue-element-admin ★26166 - vue2管理系统模板vue-admin ★9089 - Vue管理面板框架electron-vue ★8393 - Electron及VueJS快速启动样板quasar ★8299 - 响应式网站和混合移动应用程序实用库vuex ★18608 - 专为 Vue.js 应用程序开发的状态管理模式vue-lazyload ★4423 - 用于懒加载的Vue模块vue-i18n ★3807 - VueJS的多语言切换插件vue-loader ★3677 - Vue.js 针对Webpack的组件装载插件vuelidate ★3287 - 简单轻量级的基于模块的Vue.js验证vue-meta ★2165 - 管理app的meta信息Vue-Socketio ★1786 - VueJS的socketio实现vue-awesome ★1775 - VueJS字体Awesome组件vue-property-decorator ★1589 - VueJS和属性Decoratorvue-axios ★1347 - 将axios整合到VueJS的封装portal-vue ★1354 - 在组件外部渲染DOM服务端nuxt.js ★17569 - 用于服务器渲染Vue app的最小化框架应用实例koel ★9868 - 基于网络的个人音频流媒体服务vue-manage-system ★6243 - 后台管理系统解决方案pagekit ★5021 - 轻量级的CMS建站系统PJ Blog ★2194 - 开源博客goldfish ★2005 - 用于HashiCorp Vault的Admin UIDemo示例vue2-elm ★24765 - 重写饿了么webappvue2-manage ★5320 - 基于 vue + element-ui 的后台管理系统Vue-cnodejs ★3037 - 基于vue重写Cnodejs.org的webappNeteaseCloudWebApp ★2112 - 高仿网易云音乐的webappvue2-happyfri ★6304 - vue2及vuex的入门练习项目douban ★1987 - 基于vue全家桶的精致豆瓣DEMOeleme ★1526 - 高仿饿了么app商家详情vue-wechat ★1435 - vue.js开发微信app界面bilibili-vue ★1425 - 全栈式开发bilibili首页vue-WeChat ★1292 - 基于Vue2高仿微信App的单页应用vue-music ★1246 - Vue 音乐搜索播放vue-Meizi ★1185 - vue最新实战项目xyy-vue ★1180 - 大学生交流平台VueDemo_Sell_Eleme ★1231 - Vue2高仿饿了么外卖平台vue-demo ★1096 - vue简易留言板 ...

January 20, 2019 · 2 min · jiezi

css动画实现loading效果

css animation 动画实现loading状态HTML<div class=“root”></div> CSS.root { width: 25px; height: 25px; border-radius: 50px; border: 2px dashed #ddeeff; animation: loading 1s infinite linear;}@keyframes loading { to { transform: rotate(180deg) }}JavaScriptsetTimeout(() => { $(’.root’).css({ ‘animation-play-state’: ‘paused’ }) }, 5000)

January 19, 2019 · 1 min · jiezi

简单几步,用纯CSS3实现3D翻转效果!

作为前端开发人员的必修课,CSS3能带我们完成许多基本动效,本期我们将用CSS3实现hover翻转效果~第一步非常简单,我们简单画1个演示方块,为其添加transition和transform属性:// 本示例均使用Sass语法.block { width: 200px; height: 200px; background: brown; cursor: pointer; transition: 0.8s; &:hover { transform: rotateY(180deg); }}我们看一看这时候的效果:这里需要注意的是:transition属性要写在.block上而不是hover上,如果只在hover上写transition,则鼠标移出时并没有transition的过渡效果,如果我们只将transition写在hover上:第二步比较关键:我们不难发现始终在1个平面上翻转,不够有立体感,因此我们需要稍加改变思路——用2层div嵌套// html部分<div class=“block”> <div class=“block-in”></div></div>// CSS部分.block { width: 200px; height: 200px; cursor: pointer; &-in { background: brown; height: 100%; transition: 0.8s; } &:hover .block-in { transform: rotateY(180deg); }}此时效果没变,如下:这个时候关键的1步来了:我们需要给外层添加perspective和transform-style属性,为整个动画增添3D变形效果:.block { width: 200px; height: 200px; cursor: pointer; /* 3D变形 */ transform-style: preserve-3d; -webkit-perspective: 1000; -moz-perspective: 1000; -ms-perspective: 1000; perspective: 1000; &-in { background: brown; height: 100%; transition: 0.8s; } &:hover .block-in { transform: rotateY(180deg); }}最终实现效果如下:最终我们总结一下思路:1.建立内外2层div,鼠标 hover 到外层时,内层div添加翻转 transform: rotateY(180deg)2.注意将 transition 属性添加到需要翻转的div上,而不是 hover 时3.外层div添加 perspective 和 transform-style 属性,最终实现3D翻转效果 ...

January 17, 2019 · 1 min · jiezi

学习如何用CSS变量创建网页响应布局 — css var()

在创新的2018年已经过去,在2019年看看如何简单做成响应性的网站如果你未曾听说过CSS变量,那么我告诉你,它就是CSS的一种新功能,可以让你拥有在样式表中使用变量的能力,这样做时并不需要什么特别的设置呦。从本质上讲,CSS变量可以让你摆脱老式的样式设置:h1 { font-size: 30px;}navbar>a { font-size: 30px;}/ …而是主张这样写: /:root { –base-font-size: 30px;}h1 { font-size: var(–base-font-size);}navbar>a { font-size: var(–base-font-size);}这样的语法看起来的确有点怪怪的,但有没有觉得它和less、sass中的变量有点类似呢,但如此一来,只要更改–base-font-size 变量,就能在整个应用中改变字号了。如果你想把CSS变量学明白,可以在Scrimba网站 这里有免费互动CSS变量课程,该课程包含8个互动截屏。今天来讲一下如何用CSS变量创建响应布局这是一段html<ul class=“item”> <li>item 1</li> <li>item 2</li> <li>item 3</li> <li>item 4</li></ul>老方法:在以前即使不使用CSS变量也可以把这些事情搞定。只是需要把要改变的属性在重新重置一下,需要在媒体查询中拥有自己的选择器,但会招致额外的大量代码,像下面这样:.item { display: flex; flex-direction: column; flex-wrap: wrap; justify-content: space-around; padding: 10px; li { background-color: #ff6f69; border: 1px solid #fff; font-size: 20px; height: 200px; list-style-type: none; width: 100%; }}@media (min-width: 768px) { .item { flex-direction: row; li { background-color: #ffcc5c; font-size: 50px; height: 300px; width: 50%; } }}@media (min-width: 992px) { :root { –base-font-size: 100px; –base-color: #ffeead; –height: 500px; –width: calc(100% / 4); –wrap: nowrap; } .item { flex-wrap: nowrap; li { background-color: #ffeead; font-size: 100px; height: 500px; width: calc(100% / 4); } }}新方法下面让我们来看如何使用CSS变量来解决这个问题。首先,要把我们将重复利用和更改的数值存储在变量的内部::root { –base-color: #ff6f69; –base-font-size: 20px; –direction: column; –width: 100%; –height: 200px;}然后,在整个页面中简单地使用这些变量就行了:.item { display: flex; padding: 10px; justify-content: space-around; flex-direction: var(–direction); flex-wrap: var(–wrap); li{ list-style-type: none; border: 1px solid #fff; height: var(–height); width: var(–width); background-color: var(–base-color); font-size: var(–base-font-size); }}一旦进行了这样的设置之后,我们只要在媒体查询中简单地更改变量值就行了:@media (min-width: 768px) { :root { –base-font-size: 60px; –base-color: #ffcc5c; –direction: row; –height: 300px; –width: 50%; –wrap:wrap; }}@media (min-width: 992px) { :root { –base-font-size: 100px; –base-color: #ffeead; –direction: row; –height: 500px; –width: calc(100% / 4); –wrap:nowrap; }}这比我们以往的方法简便多了。只需盯住 :root,而不必为所有的选择器指定值了。这只是一个简单的例子。设想成熟的网站会是什么样子吧,例如,用 –base-margin 来控制APP四周的多数自由空间。想翻转其值也是很容易的事情,不必用复杂的选择器来填充媒体查询了。总之,CSS变量绝对是提高响应速度时,所代表的未来。查看效果作者: w3cbest前端开发 互动: 如有疑问可进群讨论本文原创,著作权归作者所有。商业转载请联系@w3cbest前端开发获得授权,非商业转载请注明原链接及出处。 ...

January 14, 2019 · 1 min · jiezi

深入理解Flex布局 -- flex-grow & flex-shrink & flex-basis

一、前言最近在项目里遇到了一个 Flex 布局的问题,才发现自己对它的理解还是停留在浅显的水平,遇到一些特殊情况就不知道如何处理。于是找了些资料深入学习一下,然后将我的学习心得总结成这篇文章。二、问题还原先讲讲我遇到的问题。我希望实现一个左中右三列的布局,其中左右部分固定宽度,中间部分自适应:实现起来很简单,代码如下:<div class=“container”> <div class=“left”>left</div> <div class=“middle”> middle </div> <div class=“right”>right</div></div>.container { display: flex; width: auto; height: 300px; background: grey;}.left { flex-basis: 200px; background: linear-gradient(to bottom right, green, white);}.middle { flex: 1; background: linear-gradient(to bottom right, yellow, white);}.right { flex-basis: 300px; background: linear-gradient(to bottom right, purple, white);}到此为止一切都很美好。但遇到中间部分内容很长的时候,UI 就变形了:为了固定住左右部分的宽度,需要给 left 和 right 加上flex-shrink: 0。但加上后容器的宽度就被撑开了,页面底部出现了滚动条:而我期望的效果是滚动条出现在中间部分,整个页面不能滚动。解决方法是给 middle 加上overflow: scroll:此时的完整代码如下:<div class=“container”> <div class=“left”>left</div> <div class=“middle”> middle <!– 宽度为800px的内容–> <div class=“long”>long</div> </div> <div class=“right”>right</div></div>.container { display: flex; width: auto; height: 300px; background: grey;}.left { flex-basis: 200px; flex-shrink: 0; background: linear-gradient(to bottom right, green, white);}.middle { flex: 1; overflow: scroll; background: linear-gradient(to bottom right, yellow, white);}.right { flex-basis: 300px; flex-shrink: 0; background: linear-gradient(to bottom right, purple, white);}.long { width: 800px;}完整的 codepen 在这里实战经验到此结束,下面我们再深入学习涉及到的知识点。三、知识点先来讲讲上面用到的属性flex: 1。它其实是一个缩写,等价于flex: 1 1 0,也就是flex-grow : 1;flex-shrink : 1;flex-basis : 0;flex-grow 表示当有剩余空间的时候,分配给项目的比例flex-shrink 表示空间不足的时候,项目缩小的比例flex-basis 表示分配空间之前,项目占据主轴的空间下面来讲讲 flex 空间分配的步骤。flex-grow(默认值 0)假设有一个宽度为 800 的容器,里面有 3 个项目,宽度分别是 100,200,300:<div class=“container”> <div class=“left”>left</div> <div class=“middle”>middle</div> <div class=“right”>right</div></div>.container { display: flex; width: 800px; height: 300px; background: grey;}.left { flex-basis: 100px; background: linear-gradient(to bottom right, green, white);}.middle { flex-basis: 200px; background: linear-gradient(to bottom right, yellow, white);}.right { flex-basis: 300px; background: linear-gradient(to bottom right, purple, white);}效果如下:这时候就出现了多余的 200 的空间(灰色部分)。这时候如果我们对左中右分别设置flex-grow为 2,1,1,各个项目的计算逻辑如下:首先将多余空间 200 除以 4(2 + 1 + 1),等于 50left = 100 + 2 x 50 = 200middle = 200 + 1 x 50 = 250right = 300 + 1 x 50 = 350flex-shrink(默认值 1)假设父容器宽度调整为 550,里面依然是 3 个项目,宽度分别是 100,200,300,这时候空间就不够用溢出了。首先要理解清楚,当我们定义一个固定宽度容器为flex的时候,flex会尽其所能不去改变容器的宽度,而是压缩项目的宽度。这时我们对左中右分别设置flex-shrink为 1,2,3,计算逻辑如下:溢出空间 = 100 + 200 + 300 - 550 = 50总权重 = 1 x 100 + 2 x 200 + 3 x 300 = 1400left = 100 - (50 x 1 x 100 / 1400) = 96.42middle = 200 - (50 x 2 x 200 / 1400) = 185.72right = 300 - (50 x 3 x 300 / 1400) = 267.86如果我们不想项目被压缩,就必须将flex-shrink设为 0。还是用上面的例子,当左中右的flex-shrink都为 0 的时候,就会冲破宽度限制,container的宽度将会从 550 变为 600。codepen 在这里flex-basis(默认值 auto)flex-basis指定项目占据主轴的空间,如果不设置,则等于内容本身的空间:四、总结本文从问题出发,讲解了Flex布局在实战中的应用,并深入到flex-grow,flex-shrink和flex-basis的细节,描述了项目空间在填充和溢出情况下的计算方式,希望对你有所帮助。 ...

January 9, 2019 · 2 min · jiezi

css权重

1、什么是css权重?css6大基础选择器css权重指的是css6大基础选择符的优先级,优先级高的css样式会覆盖优先级底的css样式,优先级越高说明权重越高,反之亦然。css6大基础选择器:a)、id选择器(#box{})b)、类选择器(.box{})c)、属性选择器(a[href=“http://www.xxx.com”])d)、伪类和伪对象选择器(:hoevr{}和::after{})e)、标签类型选择器(div{})f)、通配符选择器(*{})2、css权重计算规则计算css权重是有一定规则的,根据w3c制定的css规范,css权重计算规则如下:a)、计算选择符中的id选择器的数量 一个id选择器为一个a,一个a为100 b)、计算选择符中的类选择器、属性选择器以及伪类选择器的数量 一个类选择器、属性选择器以及伪类选择器为一个b,一个b为10 c)、计算标签类型选择器和伪对象选择器的数量 一个标签类型选择器、伪对象选择器为一个c,一个c为1 d)、忽略通配符选择器 通配符选择器忽略不计如下面这张图就是一个css选择器权重的例子:如果两个选择符的权重相同,则可依照”就进原则”来判断,最后定义的选择符会被采用。但尽量避免出现这种依靠定义的顺序决定选择符优先级的情况,因为在后续的维护中很难保证定义的顺序不会被打乱。

January 8, 2019 · 1 min · jiezi

自定义单选框样式以及获取单选框的选中状态

一、用户可选择微信支付或者支付宝支付 <div class=“checkbox”> <input @click=“getPayType” class=“choose” id=“wechat” name=“pay” type=“radio” value=“wechat_h5”> <label for=“wechat”></label> </div> <div class=“checkbox”> <input @click=“getPayType” class=“choose” id=“zhifubao” name=“pay” type=“radio” value=“alipay_h5”> <label for=“zhifubao”></label> </div>单选框知识点注意:1.必须要为input添加name属性,并且属性值都是相同的,才能实现单选框 2.type属性为radio二、不同浏览器其默认的选项不同js方法:对象.checked = true备注:微信浏览器默认选择微信支付其它浏览器默认选择支付宝支付 judgePay() { if (browser.versions.mobile && browser.versions.weixin) { // 既是移动端也是微信浏览器 document.getElementById(‘wechat’).checked = true //获取id为‘wechat’的标签,并为它添加属性checked } else if (browser.versions.mobile && !browser.versions.weixin) { // 移动端但不是微信浏览器 document.getElementById(‘zhifubao’).checked = true //同上 } }三、获取当前选中的是微信支付还是支付宝支付。为其添加value属性区分微信还是支付宝 微信:value=“wechat_h5” 支付宝:value=“alipay_h5” getPayType() { var choose = document.getElementsByClassName(‘choose’) // 获取标签数组 for (var i = 0; i < choose.length; i++) { if (choose[i].checked) { // 如果被选中则执行以下代码 console.log(choose[i].getAttribute(‘value’)) // 如果选中微信,则输出wechat_h5 反之输出 alipay_h5 } } },四、自定义选中框样式input[type=‘radio’] + label::before // 未选中的样式 content ‘\a0’ /* 不换行空格 / display inline-block vertical-align middle font-size 12px width 0.213333rem / 8/37.5 / height 0.213333rem / 8/37.5 / border-radius 50% border 1px solid #01cd78 text-indent 0.15em line-height 1 background-clip content-box padding 0.08rem / 3/37.5 /input[type=‘radio’]:checked + label::before // 选中之后的样式 background-color #01cd78 background-clip content-box padding 0.08rem / 3/37.5 */input[type=‘radio’] position absolute clip rect(0, 0, 0, 0) ...

January 7, 2019 · 1 min · jiezi

前端经典面试题CSS三栏布局

对于前端来说,布局也是必须掌握的,一个好的布局可以让页面看起来更美观。提到布局,那就不得不说CSS三栏布局。这是前端面试经常会问到的一个问题,算是基础题。所谓的三栏布局,一般是指左右两边固定中间自适应,或者是中间固定左右两边自适应。左右两边固定中间自适应圣杯布局HTML结构设置新建一个父元素,包含三个子元素:left、main、right(注意,main在写在前面,这样在页面渲染时会先加载中间,针对面试题优先加载中间部分)style样式设置1、父元素设置高度 2、三个元素均设置浮动 3、中间main部分定宽100%:width: 100%,左右两边按产品需求设置宽高 4、左边设置margin-left: -100%;右边设置margin-right: -右盒子宽 5、父元素设置padding-left: 左盒子宽;padding-right: 右盒子宽 6、左右盒子相对定位<div class=“container”> <div class=“main f”> go aheadgo aheadvgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo ahead </div> <div class=“left f”></div> <div class=“right f”></div></div><style> body { min-width: 700px; } .container { height: 300px; padding: 0 200px 0 200px; } .f { float: left; } .main { width: 100%; height: 300px; background-color: cornflowerblue; } .left { width: 200px; height: 300px; background-color: indianred; margin-left: -100%; position: relative; left: -200px; } .right { width: 200px; height: 300px; background-color: lightgreen; margin-left: -200px; position: relative; right: -200px; }</style>该布局受内部元素影响而破坏布局的概率低,但是当浏览器屏幕缩小的一定程度时,左右两侧的内容会掉下来,或发生重叠现象。解决方案,给body加一个最小宽度(起码大于左右两侧宽度之和)双飞翼布局与圣杯布局的思路是一致的,只是有一些细微的差别。HTML结构设置新建一个父元素,包含三个子元素:left、main、right(注意,main在写在前面,这样在页面渲染时会先加载中间,针对面试题优先加载中间部分)style样式设置1、父元素设置高度 2、三个元素均设置浮动 3、中间main部分定宽100%:width: 100%,左右两边按产品需求设置宽高 4、中间main部分再加一个盒子inner,放置内容(与圣杯布局的不同点) 5、左边设置margin-left: -100%;右边设置margin-right: -右盒子宽 6、新添加盒子,inner,设置左右padding或margin<div class=“container”> <div class=“main f”> <div class=inner> go aheadgo aheadvgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo ahead </div> </div> <div class=“left f”></div> <div class=“right f”></div></div><style> .container { height: 300px; } .f { float: left; } .main { width: 100%; height: 300px; background-color: cornflowerblue; } .left { width: 200px; height: 300px; background-color: indianred; margin-left: -100%; } .right { width: 200px; height: 300px; background-color: lightgreen; margin-left: -200px; } .inner { padding: 0 200px 0 200px; }</style>自身浮动HTML结构设置新建三个元素:left、right、main(注意,main写在后面)style样式设置1、左盒子左浮动,右盒子右浮动 2、中间部分设置margin值<div class=“left”></div><div class=“right”></div><div class=“main”>我是中间内容我是中间内容我是中间内容我是中间内容我是中间内容我是中间内容我是中间内容我是中间内容我是中间内容我是中间内容</div><style> .main { margin: 0 200px 0 200px; background-color: red; height: 200px; } .left { float: left; width: 200px; background-color: blue; height: 200px; } .right { float: right; width: 200px; background-color: pink; height: 200px; }</style>CSS3新特性:flexHTML结构设置新建一个父元素,包含三个子元素:left、main、right(注意,main写在中间)style样式设置1、父元素设置宽度为100%,display: flex; 2、左右两则按产品需求设置宽高 3、中间部分设置flex: 1;<div class=“container”> <div class=“left”></div> <div class=“right”></div> <div class=“main”> go aheadgo aheadvgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo aheadgo ahead </div></div><style> .container { width: 100%; height: 200px; display: flex; } .main { flex: 1; background-color: red; height: 200px; } .left { width: 200px; background-color: blue; height: 200px; } .right { width: 200px; background-color: pink; height: 200px; }</style>还有其他的写法,这里就不一一赘述,只是列举了一些比较常用的,以及面试可能会问到的情况。CSS3还有很多好玩的特性,在工作和学习的过程中值得深入研究。中间固定左右两边自适应浮动 + 负边距 (圣杯布局)HTML结构设置新建一个父元素,包含三个子元素:left、main、right(注意,main写在中间)style样式设置1、左右两边各占50%的宽度 2、左边负边距 margin-left 占中间div宽度的一半 3、右边负边距 margin-right 也占中间div宽度的一半 <div class=“container”> <div class=“left”></div> <div class=“main”>我是中间内容</div> <div class=“right”></div> </div> <style> .main { width: 100px; text-align: center; float: left; background-color: lightgreen; height: 300px; } .left { height: 300px; float: left; width: 50%; margin-left: -50px; background-color: pink; } .right { height: 300px; float: right; width: 50%; margin-right: -50px; background-color: cornflowerblue; } </style> CSS3新特性:flexHTML结构设置新建一个父元素,包含三个子元素:left、main、rightstyle样式设置1、父元素设置display: flex;flex-direction: row; 2、左右设置flex-grow: 1,平分剩余空间 <div class=“container”> <div class=“left”></div> <div class=“main”>我是中间内容</div> <div class=“right”></div> </div> <style> .container { display: flex; flex-direction : row; } .main { width: 200px; height: 300px; text-align: center; background-color: lightgreen; } .left { height: 300px; flex-grow: 1; background-color: pink; } .right { height: 300px; flex-grow: 1; background-color: cornflowerblue; } </style> CSS3特性 calc(四则运算)用于动态计算长度值。需要注意的是,运算符前后都需要保留一个空格,例如:width: calc(100% - 50px)。 <div class=“container”> <div class=“left”></div> <div class=“main”>我是中间内容</div> <div class=“right”></div> </div> .container { width: 100%; height: 300px; } .f { float: left; } .main { width: 100px; text-align: center; background-color: lightgreen; height: 300px; } .left { height: 300px; background-color: pink; width: calc(50% - 50px); /平分中间部分的宽度/ } .right { height: 300px; background-color: cornflowerblue; width: calc(50% - 50px); /平分中间部分的宽度/ } ...

January 6, 2019 · 3 min · jiezi

「前端面试题系列3」伪类与伪元素的区别及实战

前言面试前端候选人的时候,我经常会问这样一个有关CSS的问题:你知道伪类与伪元素么,它们的分别是什么?这时,能回答上来的很少。换一种问法,你知道 :hover, :active, :focus, :visited么?这时,基本都能回答上来,这不就是a标签的四种状态么。嗯,ok。然后继续问,那么 ::before 和 ::after,听说过么?这时,能听到的回答是,嗯,我看到过,偶尔会用。伪类与伪元素,都有一个“伪”字,那它们有什么区别么?这时,回应我的,是一片沉默。。。从回答上来分析,虽然伪类和伪元素平时都有接触,但在概念上,都比较模糊。今天,我们就来说说伪类与伪元素的区别,以及使用场景。伪类,不是只有a标签的四种状态。伪元素,也不是只有 ::before 与 ::after。更多的伪类与伪元素,详见文末附录。概念上的区别从概念上来区分,大致有以下几点:伪类,更多的定义的是状态。常见的伪类有 :hover,:active,:focus,:visited,:link,:not,:first-child,:last-child等等。伪元素,不存在于DOM树中的虚拟元素,它们可以像正常的html元素一样定义css,但无法使用JavaScript获取。常见伪元素有 ::before,::after,::first-letter,::first-line等等。CSS3明确规定了,伪类用一个冒号(:)来表示,而伪元素则用两个冒号(::)来表示。但目前因为兼容性的问题,它们的写法可以是一致的,都用一个冒号(:)就可以了,所以非常容易混淆。实战场景——伪类表单校验表单的校验中,常会用到 :required、:valid 和 :invalid 这三个伪类。先来看看它们所代表的含义。:required,指定具有 required属性 的表单元素:valid,指定一个 匹配指定要求 的表单元素:invalid,指定一个 不匹配指定要求 的表单元素看下面这个例子:<p>input中类型为email的校验</p><p>符合email校验规则</p><input type=“email” required placeholder=“请输入” value=“24238477@qq.com” /><br><br><p>不符合email校验规则</p><input type=“email” required placeholder=“请输入” value=“lalala” /><br><br><p>有required标识,但未填写</p><input type=“email” required placeholder=“请输入” value="" />input { &:valid { border-color: green; box-shadow: inset 5px 0 0 green; } &:invalid { border-color: red; box-shadow: inset 5px 0 0 red; } &:required { border-color: red; box-shadow: inset 5px 0 0 red; }}效果如下:折叠面板过去,要实现折叠面板的显示或隐藏,只能用JavaScript来搞定。但是现在,可以用伪类 :target 来实现。 :target 是文档的内部链接,即 URL 后面跟有锚名称 #,指向文档内某个具体的元素。看下面这个例子:<div class=“t-collapse”> <!– 在url最后添加 #modal1,使得target生效 —> <a class=“collapse-target” href="#modal1">target 1</a> <div class=“collapse-body” id=“modal1”> <!– 将url的#modal1 变为 #,使得target失效 —> <a class=“collapse-close” href="#">target 1</a> <p>…</p> </div></div>.t-collapse { >.collapse-body { display: none; &:target { display: block; } }}元素的index当我们要指定一系列标签中的某个元素时,并不需要用JavaScript获取。可以用 :nth-child(n) 与 :nth-of-type(n) 来找到,并指定样式。但它们有一些小区别,需要注意。首先,它们的n可以是大于零的数字,或者类似2n+1的表达式,再或者是 even / odd。另外,还有2个区别::nth-of-type(n) 除了关注n之外,还需要关注最前面的类型,也就是标签。:nth-child(n) 它关注的是:其父元素下的第n个孩子,与类型无关。看下面这个例子,注意两者的差异:<h1>这是标题</h1><p>第一个段落。</p><p>第二个段落。</p><p>第三个段落。</p><p>第四个段落。</p><p>第五个段落。</p>实战场景——伪元素antd的彩蛋事件还记得2018年圣诞节的“彩蛋事件”,在整个前端圈,轰动一时。因为按钮上的一朵云,导致不少前端er提前回家过年了。当时,彩蛋事件出现的第一时间,就吓得我赶快打开工程看了一眼,果然也中招了。为了保住饭碗,得赶紧把云朵去掉。查看了生成的html,发现原来是 button 下藏了一个 ::before。所以,赶紧把样式覆盖掉,兼容代码如下:.ant-btn { &::before { display: none !important; }}美化选中的文本在网页中,默认的划词效果是,原字色保持不变,划过时的背景变为蓝底色。其实,这是可以用 ::selection 来进行美化的。看下面这个例子:<p>Custom text selection color</p>::selection { color: red; background-color: yellow;}效果如下:划过的部分美化为:红色的字体,并且底色变为了黄色。总结CSS也可以实现动态的交互,并非只有JavaScript才能实现。书写的时候,要尊重规范。写伪类的时候用 :,而写伪元素的时候用 ::。兼容性的问题,交给postcss去做。本文并未涉及兼容性的写法,包括前缀问题,可以交给autoprefixer去做。附录CSS3中的伪类:root 选择文档的根元素,等同于 html 元素:empty 选择没有子元素的元素:target 选取当前活动的目标元素:not(selector) 选择除 selector 元素意外的元素:enabled 选择可用的表单元素:disabled 选择禁用的表单元素:checked 选择被选中的表单元素:nth-child(n) 匹配父元素下指定子元素,在所有子元素中排序第n:nth-last-child(n) 匹配父元素下指定子元素,在所有子元素中排序第n,从后向前数:nth-child(odd) 、 :nth-child(even) 、 :nth-child(3n+1):first-child 、 :last-child 、 :only-child:nth-of-type(n) 匹配父元素下指定子元素,在同类子元素中排序第n:nth-last-of-type(n) 匹配父元素下指定子元素,在同类子元素中排序第n,从后向前数:nth-of-type(odd) 、 :nth-of-type(even) 、 :nth-of-type(3n+1):first-of-type 、 :last-of-type 、 :only-of-typeCSS3中的伪元素::after 已选中元素的最后一个子元素::before 已选中元素的第一个子元素::first-letter 选中某个款级元素的第一行的第一个字母::first-line 匹配某个块级元素的第一行::selection 匹配用户划词时的高亮部分PS:欢迎关注我的公众号 “超哥前端小栈”,交流更多的想法与技术。 ...

January 6, 2019 · 1 min · jiezi

css多种方式实现等宽布局

本文讲的等宽布局是在不手动设置元素宽度的情况下,使用纯css实现各个元素宽度都相当的效果。1、使用table-cell实现(兼容ie8)<style> body,div{ margin: 0; padding: 0; } .table-layout{ display: table;/父元素必须设置为table/ table-layout: fixed;/这个属性一定要有,否则达不到效果/ width: 50%; margin: 20px auto; } .table-cell-layout{ display: table-cell;/子元素必须设置为table-cell/ height: 40px; border: 1px solid #666; border-left: none; } .table-cell-layout:first-child{ border-left: 1px solid #666; }</style><body> <ul class=“table-layout”> <li class=“table-cell-layout”>li1</li> <li class=“table-cell-layout”>li2</li> <li class=“table-cell-layout”>li3</li> <li class=“table-cell-layout”>li4</li> <li class=“table-cell-layout”>li5</li> </ul></body>2、使用flex布局来实现<style> body,div{ margin: 0; padding: 0; } .flex-layout{ display: flex; width: 50%; margin: 20px auto; } .flex-item{ flex: 1; height: 40px; border: 1px solid #666; border-left: none; } .flex-item:first-child{ border-left: 1px solid #666; }</style><body> <ul class=“flex-layout”> <li class=“flex-item”>li1</li> <li class=“flex-item”>li2</li> <li class=“flex-item”>li3</li> <li class=“flex-item”>li4</li> <li class=“flex-item”>li5</li> </ul></body> ...

January 4, 2019 · 1 min · jiezi

HTML+CSS的两栏、三栏布局以及垂直居中

这一次我想讲解一下HTML+CSS的两栏、三栏布局以及垂直居中的实现方式。因为个人所学有限所以可能不会罗列出所有的实现方法,不过我会继续努力查漏补缺。1.两栏布局(左固定,右适应)先写出初始样式和结构。<div class=“container”> <div class=“left”>Lorem ipsum dolor sit amet</div> <div class=“right”>Lorem ipsum dolor sit amet</div></div>div { height: 200px; color: #fff;}float+margin实现方式.left { float: left; width: 300px; background-color: #5616;}.right { width: 100%; margin-left: 300px; background-color: #438; }position实现方式.left { position: absolute; left: 0; width: 300px; background-color: #5616;}.right { width: 100%; margin-left: 300px; background-color: #438; }flex.container { display: flex;}.left { flex: 0 0 300px; background-color: #5616;}.right { flex: 1 1; background-color: #438; }右固定,左适应同理。2.三栏布局float + margin方式<div class=“container”> <div class=“left”>Lorem ipsum dolor sit amet</div> <div class=“right”>Lorem ipsum dolor sit amet</div> <div class=“main”>Lorem ipsum dolor sit amet</div></div>div { height: 200px; color: #fff;}.main { width: 100%; margin-left: 300px; margin-right: 100px; background-color: #554;}.left { float: left; width: 300px; background-color: #5616;}.right { float: right; width: 100px; background-color: #438; }position实现方式.main { width: 100%; margin-left: 300px; margin-right: 100px; background-color: #554;}.left { position: absolute; left: 0px; width: 300px; background-color: #5616;}.right { position: absolute; right: 0px; width: 100px; background-color: #438; }以上这些实现方式,虽然实现了但还不够好。因为main是主要的显示区域,所以我们应该先加载它再加载其它的地方。grid实现方式.container { display: grid; grid-template-columns: 300px auto 100px; //列的宽度}.main { grid-row: 1; //第几行 background-color: #554;}.left { grid-row: 1; //第几行 background-color: #5616;}.right { grid-row: 1; //第几行 background-color: #438; }圣杯布局.container { padding: 0 100px 0 300px; overflow: hidden;}.main { float: left; width: 100%; background-color: #554;}.left { position: relative; float: left; width: 300px; left: -300px; margin-left: -100%; background-color: #5616;}.right { position: relative; float: left; right: -100px; margin-left: -100px; width: 100px; background-color: #438; }双飞翼布局<div class=“container”> <div class=“wrap”> <div class=“main”>Lorem ipsum dolor sit amet</div> </div> <div class=“left”>Lorem ipsum dolor sit amet</div> <div class=“right”>Lorem ipsum dolor sit amet</div></div>div { height: 200px; color: #fff;}.wrap { float: left; width: 100%;}.main { margin: 0 100px 0 300px; overflow: hidden; background-color: #554;}.left { float: left; width: 300px; margin-left: -100%; background-color: #5616;}.right { float: left; width: 100px; margin-left: -100px; background-color: #438; }两种布局方式的不同之处在于如何处理中间主列的位置:圣杯布局是利用父容器的左、右内边距+两个从列相对定位;双飞翼布局是把主列嵌套在一个新的父级块中利用主列的左、右外边距进行布局调整3.垂直居中position + margin实现(1)<div class=“container”> <div class=“content”></div></div>.container { position: relative; width: 500px; height: 500px; background-color: #5465;}.content { position: absolute; left: 50%; top: 50%; width: 200px; height: 200px; margin-left: -100px; margin-top: -100px; background-color: #6465;}position + margin实现(2).container { position: relative; width: 500px; height: 500px; background-color: #5465;}.content { position: absolute; left: 0; top: 0; bottom: 0; right: 0; width: 200px; height: 200px; margin: auto; background-color: #6465;}position + transform实现.container { position: relative; width: 500px; height: 500px; background-color: #5465;}.content { position: absolute; left: 50%; top: 50%; width: 200px; height: 200px; transform: translate(-50%, -50%); background-color: #6465;}flex实现.container { display: flex; align-items: center; justify-content: center; width: 500px; height: 500px; background-color: #5465;}.content { width: 200px; height: 200px; background-color: #6465;}inline-block实现.container { display: inline-block; width: 500px; height: 500px; text-align: center; background-color: #5465;}.content { display: inline-block; width: 200px; height: 200px; vertical-align: middle; background-color: #6465;}.container::after{ content: ‘’; display: inline-block; width: 0; height: 100%; vertical-align: middle;}效果都如下 ...

January 4, 2019 · 2 min · jiezi

3D文本悬停改变效果

html<h1 class=“hover-text-3d” data-text=“w3cbest.com”>W3CBEST.COM</h1>css.hover-text-3d { font-size: 7em;}.hover-text-3d { position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%); -o-transform: translate(-50%, -50%); transform: translate(-50%, -50%); -webkit-text-fill-color: #fff; text-fill-color: #fff;}.hover-text-3d:before { position: absolute; overflow: hidden; width: 0; content: attr(data-text); -webkit-transition: 2s; -o-transition: 2s; transition: 2s; -webkit-text-fill-color: #aaf0d1; text-fill-color: #aaf0d1; -webkit-text-stroke-width: 2px; text-stroke-width: 2px; -webkit-text-stroke-color: #aaf0d1; text-stroke-color: #aaf0d1; -webkit-filter: drop-shadow(5px 5px 5px rgba(0, 0, 0, .8)); -o-filter: drop-shadow(5px 5px 5px rgba(0, 0, 0, .8)); filter: drop-shadow(5px 5px 5px rgba(0, 0, 0, .8));}.hover-text-3d:hover:before { width: 100%;}查看效果 ...

January 3, 2019 · 1 min · jiezi

精读《国际化布局 - Logical Properties》

1 引言“一带一路” 正在积极推动中国的国际化进程,前端网站也面临着前所未有的国际化挑战。那么怎么才能积极响应 “一带一路” 战略,推动网站的国际化工作呢?可以先从国际化布局开始考虑。本周精读的文章是:new-css-logical-properties,通过一种新的 CSS 技术,实现国际化布局。CSS Logical Properties 是一种新的 CSS 布局方案,嗯对,和几年前的 Flex 布局、Grid 布局一样,CSS Logical Properties 方案不出意外的受到了微软的阻挠:不过没关系,不论是 Flex、Grid 我们都挺过来了,Proxy 虽然还不被微软支持,不过已经在 Edge 被支持了。相信 CSS Logical Properties 也一样,现在可以率先使用在国外环境,国内等若干年后 Edge 支持或者被淘汰了,就可以用上了。2 概述旧的盒子模型告诉我们左右上下这四个方向,但在新的模型中,请记住 inline-start inline-end block-start block-end:(LTR)对应关系如下:左: inline-start右: inline-end上: block-start下: block-end这些适用于 margin padding border 修饰,比如 margin-left 中,left -> 左 -> inline-start -> margin-inline-start这有点像把坐标系概念引入了布局,对于不同国家,inline 与 block 的方向是不同的:在东亚绝大多数国家、英美系国家 padding-inline-start = padding-left在阿拉伯国家 padding-inline-start = padding-right在日本 padding-inline-start = padding-top以中国和英美系国家的阅读顺序为基准的话,阿拉伯国家等于把左右颠倒了,而日本是把网页沿顺时针旋转 90 度。为什么 inline 表示从左右,block 表示上下呢?还记得 display: inline 吗?此时排版是从左到右排布的,而 display: block 的排版是从上到下的。宽高width height 也需要换成 inline-size 与 block-size,整理如下(LTR):width: inline-sizemin-width: min-inline-sizemax-width: max-inline-sizeheight: block-sizemin-height: min-inline-sizemax-height: max-inline-size下图是 Box Model 与 Logical 的对比:绝对定位对于绝对定位属性 top/right/left/bottomtop: inset-block-startbottom: inset-block-endleft: inset-inline-startright: inset-inline-end记得方式与 上下左右 表相同,在前面加上 inset 前缀。尽管这样描述起来很复杂:.popup { position: fixed; inset-block-start: 0; /top - in English/ inset-block-end: 0; /bottom - in English/ inset-inline-start: 0; /left - in English/ inset-inline-end: 0; /right - in English/}但是这种属性支持聚合写法:.popup { position: fixed; inset: 0 0 0 0; /top, right, bottom, left - in English/}Float对于 float 的两个值 left right,可以很容易推测出来,会被 inline-start 与 inline-end 取代(LTR):float: left = float: inline-startfloat: right = float: inline-endText-aligntext-align 也有 left right 属性,分别取代为 start end(LTR):text-align :left = text-align: starttext-align :right = text-align: endCss Grid 与 Flexbox使用 css grid 与 flexbox 布局方案的网页,将在支持的浏览器上自动享受国际化布局调整,不需要改变语法。Writing-mode目前为止,看到的是 Css 对排版含义的规范化,Grid 与 Flexbox 由于 API 比较新,定义的较为规范,所以不用变,而旧的 display, position, width, height, float 等 API 需要进行语义化改造。现在就要聊到最关键的布局国际化部分,我们至今为止遇到的网页都是从上到下的,但其他文化却不同。可以通过配置 writing-mode 让整个网页布局改变:writing-mode: horizontal-tb = 从上到下writing-mode: vertical-rl = 从右到左 比如日本文化writing-mode: vertical-lr = 从左到右 比如蒙古文化至今还没有见过从下到上的网页,也许这证明了从下到上是最不合理的阅读方式。Direction这是一个排版属性,writing-mode 是控制网页方向的,而 direction 是控制文字对齐方向的。目前只有两个配置:rtl 与 ltr:html { direction: rtl;}其实 writing-mode 与 direction 结合起来也没什么问题,比如网页布局变成 vertical-rl - 从右到左,那么 direction 的 ltr 就等于是从上到下了。最后还有一些悬而未决的问题,比如如何开启智能布局?一种方式是:html { flow-mode: physical; /or/ flow-mode: logical;}另外,像 @meta 配置中的 max-width 也要替换为 max-inline-size, line-height 需要被替换为 line-size,border-width 需要被替换为 border-size 等等。3 精读整个 Logical Properties 规范看下来是个不可逆的趋势,也代表着 W3C 规范在排版方面的全球化工作。为什么要改造语法第一个问题就是这个,我们习以为常的 left top right bottom 语法都需要改成 inline-start block-end 等略微晦涩的语法,而且你可以发现,新语法与旧语法是完全一对一对等的,也就是完全可以交给某个转换程序去做!可以看出,这是一个习惯问题,W3C 希望重塑国际化布局的语义,而原有的 left top 等无法承担这些语义,所以只好换掉。新版规范要求开发者做出一个抽象,把自己国家的习惯抽象成习惯无关的描述。但对于每个前端从业者来说,left top 等描述估计已经成为肌肉记忆了,想要改变规范还是挺难的,未来前端社区也许会出现三种解决方案:保守派 - 利用 babel 将原有语法与新语法做一对一映射转换,比如 position: left -> position: inset-inline-start。这种方案 成本最小,且不改变开发者习惯,所以最有可能被国内公司率先采用。在商业环境推动一件事情,最大的阻力无非是 成本 与 共识,这次的布局规范同时触及了这两个点,可能让团队倾向于做保守派。兼容派 - 其实就是两面派,利用 babel 工具做映射这一点与保守派相同,但是新代码推荐用新语法编写,如果团队中有人不遵循新规范,也会被工具自动转换为新规范。这种软要求会导致团队布局代码存在两套,但最终效果却没有问题的神奇效果,长远来说不利于维护,但不失为一种较为妥协的策略。改革派 - 利用脚本,将项目里旧规范替换成新规范,并让团队未来的代码遵循新的布局规范编写。很显然,这派抓住了迁移成本小这个优势,但没有考虑到人这个因素的习惯迁移成本,如何说服其他人理解新规范,并做到让 “未来加入的同事” 也能认同并遵循这套新规范,也许是最大的不确定因素。为什么 Flex Grid 语法不需改造?这次改造是冲着 left right width height 等明显带有文化色彩的语法来的。然而 Flex 语法已经将方向定义转化为抽象的 start 与 end,而 center 是没有歧义的,所以 FlexBox 语法不用改。而 Grid 是一种拆分单元格的语法,也不涉及具体上下左右的描述,所以也符合国际化语义。4 总结那么为什么 W3C 到现在才改语法,难道以前没有想到吗?也许还真是,或者处于推广成本的考量,或者当时的文明发展阶段还没有意识到文化差异会导致布局方式有所不同。当出现 Logical Properties 特性时,说明人类的全球化已经突破了翻译维度,开始向比如布局方式等其它维度蔓延了。除了布局需要国际化,使用数字的习惯也需要国际化,可以阅读这篇拓展文章 和欧洲人打交道一定要知道他们数字写法,否则吃大亏!。那么除了这些,还有哪些维度的国际化策略呢?除了语言的翻译,国际化还有哪些工作需要准备?欢迎在下面留言。讨论地址是:精读《国际化布局 - Logical Properties》 · Issue #121 · dt-fe/weekly如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。 ...

January 2, 2019 · 2 min · jiezi

「前端早读君008」新手必踩坑之display: inline-block

今日励志语往日不可追,来日犹可期,祝大家2019年继往开来迷之间隙我们创建一个导航列表,并将其列表 item 设置为 inline-block,主要代码如下:<div class=“nav”> <div class=“nav-item”><a>我</a></div> <div class=“nav-item”><a>我</a></div> <div class=“nav-item”><a>我</a></div></div>.nav { background: #999;}.nav-item{ display:inline-block; /* 设置为inline-block / width: 100px; background: #ddd;}效果图如下:我们从效果图中可以看到列表 item 之间有一点小空隙,但是我们在代码中并没有设置 margin 水平间距。那么这个空隙是如何产生的呢?这是因为我们编写代码时输入空格、换行都会产生空白符。而浏览器是不会忽略空白符的,且对于多个连续的空白符浏览器会自动将其合并成一个,故产生了所谓的间隙。对于上面实例,我们在列表 item 元素之间输入了回车换行以方便阅读,而这间隙正是这个回车换行产生的空白符。同样对于所有的行内元素(inline,inline-block),换行都会产生空白符的间隙。如何消除空白符从上面我们了解到空白符,是浏览器正常的表现行为。但是对于某些场景来说,并不美观,而且间隙大小非可控,所以我们往往需要去掉这个空白间隙。一般来说我们有两种方法来去掉这个换行引起间隙:代码不换行和设置 font-size。代码不换行我们了解到,由于换行空格导致产生换行符,因此我们可以将上述例子中的列表 item 写成一行,这样空白符便消失,间隙就不复存在了。其代码如下:<div class=“nav”> <div class=“nav-item”>导航</div><div class=“nav-item”>导航</div><div class=“nav-item”>导航</div></div>但考虑到代码可读及维护性,我们一般不建议连成一行的写法。设置 font-size首先要理解空白符归根结底是个字符,因此,我们可以通过设置 font-size 属性来控制产生的间隙的大小。我们知道如果将 font-size 设置为 0,文字字符是没法显示的,那么同样这个空白字也没了,间隙也就没了。于是顺着这个思路就有了另一个解决方案:通过设置父元素的 font-size 为 0 来去掉这个间隙,然后重置子元素的 font-size,让其恢复子元素文字字符。所以该方法代码如下:.nav { background: #999; font-size: 0; / 空白字符大小为0 /}.nav-item{ display:inline-block; width: 100px; font-size: 16px; / 重置 font-size 为16px*/ background: #ddd;}使用该方法时需要特别注意其子元素一定要重置 font-size,不然很容易掉进坑里(文字显示不出来)。对齐问题由于 inline-block 属于行内级元素,所以 vertical-align 属性同样对其适用。在正式讲解 vertical-align 之前,我们需要先说一些基本概念。中线、基线、顶线、底线中线(middle)、基线(baseline)、顶线(text-top、底线(text-bottom))是文本的几个基本线,其对应位置如下图:基线(base line):小写英文字母x的下端沿。中线(middle line):小写英文字母x的中间。顶线(text-top):父元素 font-size 大小所组成的一个内容区域的顶部底线(text-bottom):父元素 font-size 大小所组成的一个内容区域的底部vertical-align 的值vertical-align 只接受8个关键字、一个百分数值或者一个长度值。下面我们将看看各关键字如何作用于行内元素。baseline 默认元素的基线与父元素的基线对齐。sub 将元素的基线与其父元素的下标基线对齐。super 将元素的基线与其父代的上标 - 基线对齐。text-top 将元素的顶部与父元素的字体顶部对齐。text-bottom 将元素的底部与父元素的字体的底部对齐。middle 将元素的中间与基线对齐加上父元素的x-height的一半。top 将元素的顶部和其后代与整行的顶部对齐。bottom 将元素的底部和其后代与整行的底部对齐。<length> 将元素的基线对准给定长度高于其父元素的基线。<percentage> 像<长度>值,百分比是line-height属性的百分比。打开微信扫一扫关注早读君,每天早晨为你推送前端知识,度过挤地铁坐公交的时光 ...

January 2, 2019 · 1 min · jiezi

css3实现元素环绕中心点布局

效果如图:代码实现:<style> { margin: 0; padding: 0; box-sizing: border-box; } .surround-box, .center-point{ position: absolute; top: 50%; left: 50%; width: 20px; height: 20px; margin-left: -10px; margin-top: -10px; border-radius: 50%; background-color: #000; } .circle{ / 这里一定要绝对定位,这样位置才能铺开来 / position: absolute; top: -10px; left: -10px; width: 40px; height: 40px; line-height: 40px; border-radius: 50%; text-align: center; color: #fff; } .circle1{ background-color: red; / rotateZ控制方向,每个元素旋转30度,12个元素刚好360度。translateY控制每个元素距中心点的距离 */ transform: rotateZ(30deg) translateY(80px); } .circle2{ background-color: orange; transform: rotateZ(60deg) translateY(80px); } .circle3{ background-color: yellow; transform: rotateZ(90deg) translateY(80px); } .circle4{ background-color: green; transform: rotateZ(120deg) translateY(80px); } .circle5{ background-color: seagreen; transform: rotateZ(150deg) translateY(80px); } .circle6{ background-color: blue; transform: rotateZ(180deg) translateY(80px); } .circle7{ background-color: purple; transform: rotateZ(210deg) translateY(80px); } .circle8{ background-color: lightsalmon; transform: rotateZ(240deg) translateY(80px); } .circle9{ background-color: deeppink; transform: rotateZ(270deg) translateY(80px); } .circle10{ background-color: lightyellow; transform: rotateZ(300deg) translateY(80px); } .circle11{ background-color: lightgreen; transform: rotateZ(330deg) translateY(80px); } .circle12{ background-color: lightslategrey; transform: rotateZ(360deg) translateY(80px); }</style><body> <div class=“center-point”></div> <div class=“surround-box”> <div class=“circle circle1”>1</div> <div class=“circle circle2”>2</div> <div class=“circle circle3”>3</div> <div class=“circle circle4”>4</div> <div class=“circle circle5”>5</div> <div class=“circle circle6”>6</div> <div class=“circle circle7”>7</div> <div class=“circle circle8”>8</div> <div class=“circle circle9”>9</div> <div class=“circle circle10”>10</div> <div class=“circle circle11”>11</div> <div class=“circle circle12”>12</div> </div></body> ...

December 31, 2018 · 1 min · jiezi

「前端早读君007」css进阶之彻底理解视觉格式化模型

今日励志不论你在什么时候开始,重要的是开始之后不要停止。前言对于部分前端工程师来讲,有时候CSS令他们很头疼,明明设置了某个样式,但是布局就是不起作用。如果你也有这种问题,那么是时候学习下什么是css视觉格式化模型了。知己知彼方能解决问题。CSS 视觉格式化模型(visual formatting model)是用来处理和在视觉媒体上显示文档时使用的计算规则。该模型是 CSS 的基础概念之一。视觉格式化模型会根据CSS盒子模型将文档中的元素转换为一个个盒子,每个盒子的布局由以下因素决定:盒子的尺寸:精确指定、由约束条件指定或没有指定盒子的类型:行内盒子(inline)、行内级盒子(inline-level)、原子行内级盒子(atomic inline-level)、块盒子(block)定位方案(positioning scheme):普通流定位、浮动定位或绝对定位文档树中的其它元素:即当前盒子的子元素或兄弟元素视口尺寸与位置所包含的图片的尺寸其他的某些外部因素该模型会根据盒子的包含块(containing block)的边界来渲染盒子。通常,盒子会创建一个包含其后代元素的包含块,但是盒子并不由包含块所限制,当盒子的布局跑到包含块的外面时称为溢出(overflow)盒子生成介绍盒子的生成是 CSS 视觉格式化模型的一部分,用于从文档元素生成盒子。盒子有不同的类型,不同类型的盒子的格式化方法也有所不同。盒子的类型取决于 CSS 的display属性。块级元素与块盒子当元素的 display为 block、list-item或 table时,该元素将成为块级元素。一个块级元素会被格式化成一个块(例如文章的一个段落),默认按照垂直方向依次排列。每个块级盒子都会参与块格式化上下文(block formatting context)的创建,而每个块级元素都会至少生成一个块级盒子,即主块级盒子(principal block-level box)。有一些元素,比如列表项会生成额外的盒子来放置项目符号,而那些会生成列表项的元素可能会生成更多的盒子。不过,多数元素只生成一个主块级盒子。主块级盒子包含由后代元素生成的盒子以及内容,同时它也会参与定位方案。一个块级盒子可能也是一个块容器盒子。块容器盒子(block container box)要么只包含其它块级盒子,要么只包含行内盒子并同时创建一个行内格式化上下文(inline formatting context)。能够注意到块级盒子与块容器盒子是不同的这一点很重要。前者描述了元素与其父元素和兄弟元素之间的行为,而后者描述了元素跟其后代之间的行为。有些块级盒子并不是块容器盒子,比如表格;而有些块容器盒子也不是块级盒子,比如非替换行内块和非替换表格单元格。一个同时是块容器盒子的块级盒子称为块盒子(block box)。匿名块盒子在某些情况下进行视觉格式化时,需要添加一些增补性的盒子,这些盒子不能用CSS选择符选中,因此称为匿名盒子(anonymous boxes)。CSS选择器不能作用于匿名盒子(anonymous boxes),所以它不能被样式表赋予样式。也就是说,此时所有可继承的 CSS 属性值都为 inherit ,而所有不可继承的 CSS 属性值都为 initial。块包含盒子可能只包含行内级盒子,也可能只包含块级盒子,但通常的文档都会同时包含两者,在这种情况下,就会在相邻的行内级盒子外创建匿名块盒子。示例节考虑下面的HTML代码,假设 和 都保持默认的样式(即它们的 display 为 block):<div> Some inline text <p>followed by a paragraph</p> followed by more inline text.</div>此时会产生两个匿名块盒子:一个是 元素前面的那些文本(Some inline text),另一个是元素后面的文本(followed by more inline text.)。此时会生成下面的块结构:显示为:Some inline textfollowed by a paragraphfollowed by more inline text.对这两个匿名盒子来说,程序员无法像<p>元素那样控制它们的样式,因此它们会从<div>那里继承那些可继承的属性,如 color。其他不可继承的属性则会设置为 initial,比如,因为没有为它们指定 background-color,因此其具有默认的透明背景,而 元素的盒子则能够用CSS指定背景颜色。类似地,两个匿名盒子的文本颜色总是一样的。另一种会创建匿名块盒子的情况是一个行内盒子中包含一或多个块盒子。此时,包含块盒子的盒子会拆分为两个行内盒子,分别位于块盒子的前面和后面。块盒子前面的所有行内盒子会被一个匿名块盒子包裹,块盒子后面的行内盒子也是一样。因此,块盒子将成为这两个匿名块盒子的兄弟盒子。如果有多个块盒子,而它们中间又没有行内元素,则会在这些盒子的前面和后面创建两个匿名块盒子。行内级元素和行内盒子节如果一个元素的display属性为inline、inline-block或inline-table,则称该元素为行内级元素。显示时,它不会生成内容块,但是可以与其他行内级内容一起显示为多行。一个典型的例子是包含多种格式内容(如强调文本、图片等)的段落,就可以由行内级元素组成。行内级元素会生成行内级盒子,该盒子同时会参与行内格式化上下文(inline formatting context)的创建。行内盒子既是行内级盒子,也是一个其内容会参与创建其容器的行内格式化上下文的盒子,比如所有具有display:inline样式的非替换盒子。如果一个行内级盒子的内容不参与行内格式化上下文的创建,则称其为原子行内级盒子。而通过替换行内级元素或display值为inline-block或inline-table的元素创建的盒子不会像行内盒子一样可以被拆分为多个盒子。注意:开始的时候,原子行内级盒子叫做原子行内盒子,这并不准确,因为它们并不是行内盒子。后来在一次勘误时修正了这一问题。不过,当你见到某些文章中使用了“原子行内盒子”的时候,你尽可以将其理解为“原子行内级盒子”,因为这仅仅是一个名字的修改。在同一个行内格式化上下文中,原子行内级盒子不能拆分成多行:<style> span { display: inline; /* default value*/}</style><div style=“width:20em;"> The text in the span <span>can be split in several lines as it</span> is an inline box.</div>可能会显示为:The text in the span can be split into severallines as it is an inline box.而:<style> span { display: inline-block;}</style><div style=“width:20em;"> The text in the span <span> cannot be split in several lines as it </span> is an inline-block box.</div>则可能显示为:The text in the span cannot be split into several lines as it is aninline-block box.其中的“cannot be split into several lines as it”永远不会换行。匿名行内盒子类似于块盒子,CSS引擎有时候也会自动创建一些行内盒子。这些行内盒子无法被选择符选中,因此是匿名的,它们从父元素那里继承那些可继承的属性,其他属性保持默认值initial。一种常见的情况是CSS引擎会自动为直接包含在块盒子中的文本创建一个行内格式化上下文,在这种情况下,这些文本会被一个足够大的匿名行内盒子所包含。但是如果仅包含空格则有可能不会生成匿名行内盒子,因为空格有可能会由于white-space的设置而被移除,从而导致最终的实际内容为空。其他类型的盒子行盒子行盒子由行内格式化上下文创建,用来显示一行文本。在块盒子内部,行盒子总是从块盒子的一边延伸到另一边(译注:即占据整个块盒子的宽度)。当有浮动元素时,行盒子会从向左浮动的元素的右边缘延伸到向右浮动的元素的左边缘。行盒子更多是以技术性目的而存在的,Web开发者通常不需要关心。Run-in 盒子Run-in 盒子通过display:run-in来定义,它可以是块盒子,也可以是行内盒子,这取决于紧随其后的盒子的类型。Run-in 盒子可以用来在可能的情况下将标题嵌入文章的第一个段落中。注意:Run-in 盒子已经在CSS 2.1的标准中移除了,但可能会在CSS 3中作为一个实验性的内容再次加入。因此最好不要将其用于正式项目。由其他模型引入的盒子除了行内格式化上下文和块格式化上下文之外,CSS还定义了几种内容模型,这些模型同样可以应用于元素。这些模型一般用来描述布局,它们可能会定义一些额外的盒子类型:表格内容模型可能会创建一个表格包装器盒子和一个表格盒子,以及多个其他盒子如表格标题盒子等多列内容模型可能会在容器盒子和内容之间创建多个列盒子实验性的网格内容模型或flex-box内容模型同样会创建一些其他种类的盒子定位规则一旦生成了盒子以后,CSS引擎就需要定位它们以完成布局。下面是定位盒子时所使用的规则:普通流:按照次序依次定位每个盒子浮动:将盒子从普通流中单独拎出来,将其放到外层盒子的某一边绝对定位:按照绝对位置来定位盒子,其位置根据盒子的包含元素所建立的绝对坐标系来计算,因此绝对定位元素有可能会覆盖其他元素普通流介绍在普通流中,盒子会依次放置。在块格式化上下文中,盒子在垂直方向依次排列;而在行内格式化上下文中,盒子则水平排列。当CSS的 position 属性为 static 或 relative,并且 float 为 none 时,其布局方式为普通流。浮动介绍在浮动定位中,浮动盒子会浮动到当前行的开始或尾部位置。这会导致普通流中的文本及其他内容会“流”到浮动盒子的边缘处,除非元素通过 clear 清除了前面的浮动。一个盒子的 float 值不为 none,并且其 position 为 static 或 relative 时,该盒子为浮动定位。如果将 float 设置为 left,浮动盒子会定位到当前行盒子的开始位置(左侧),如果设置为 right,浮动盒子会定位到当前行盒子的尾部位置(右侧)。不管是左浮动还是右浮动,行盒子都会伸缩以适应浮动盒子的大小。绝对定位介绍在绝对定位中,盒子会完全从当前流中移除,并且不会再与其有任何联系(译注:此处仅指定位和位置计算,而绝对定位的元素在文档树中仍然与其他元素有父子或兄弟等关系),其位置会使用 top、bottom、left 和 right相对其包含块进行计算。如果元素的 position 为 absolute 或 fixed,该元素为绝对定位。对固定位置的元素来说,其包含块为整个视口,该元素相对视口进行绝对定位,因此滚动时元素的位置并不会改变。参考资料MDN文档:https://developer.mozilla.org…你可能喜欢打开微信扫一扫关注早读君,每天早晨为你推送前端知识,度过挤地铁坐公交的时光 ...

December 31, 2018 · 1 min · jiezi

css3动画transition && animation

css 动画:transition(过渡)因为有些属性动画无意义,所以可动画属性集是一个有限集合其属性为如下几部分:transition-property:指定哪个或哪些 CSS 属性用于过渡transition-duration:指定过渡的时长。或者为所有属性指定一个值,或者指定多个值,为每个属性指定不同的时长transition-timing-function:指定一个函数(根据四个点确定一个bezier曲线),定义属性值怎么变化,值;也可以从 Easing Functions Cheat Sheet 选择缓动效果bezier 曲线定义可以具体看MDN上的timing functions cubic-bezier 又称三次贝塞尔,主要是为 animation 生成速度曲线的函数,规定是 cubic-bezier(<x1>, <y1>, <x2>, <y2>)。P0:默认值 (0, 0)P1:动态取值 (x1, y1)P2:动态取值 (x2, y2)P3:默认值 (1, 1)大概效果图–p3的坐标本应该为(1,1),这是css默认的(tips:偷不到图,自己又不想画)横坐标(abscissas)范围必须是[0, 1],纵坐标(ordinate)范围如果超出[0, 1],会有弹跳效果–由于兼容性的问题,这个效果还是别考虑了transition-delay: 指定延迟,即属性开始变化时与过渡开始发生时之间的时长在使用过程中简写:transition: <property> <duration> <timing-function> <delay>;/* 分开写,可以不一一对应,如4个property对应一个duration /.transition { position: absolute; top: 20px; left: 20px; width: 40px; height: 40px; border-radius: 50%; background-color: red; / transition-property: left color width height; transition-duration: 1.2s; transition-timing-function: cubic-bezier(0.23, 1, 0.32, 1); transition-delay: 80ms; */ transition: all 1.2s cubic-bezier(0.23, 1, 0.32, 1) 80ms; &–move { left: 400px; background-color: cornflowerblue; width: 100px; height: 100px; transform: translateY(100px) }}检测过渡是否完成,这样就可以将css过渡和js动画结合起来el.addEventListener(“transitionend”, updateTransition, true);所以,所有property指定的属性,在值发生改变的的时候,都会遵循duration、timing-function、delay等定义的过度效果达到最后一帧的状态,并保持下来,至于transform,和width等一样,只是一个改变时可以触发transition过渡效果的可动画属性集合中的一个属性,transition,transform基本兼容到ie10。css 动画:animation(动画)属性描述animation-name动画名称animation-duration一个周期的时间(ms \s)animation-timing-function缓冲效果函数animation-delay动画执行前延迟时间(ms \s)animation-iteration-count动画执行的次数animation-direction动画是否反向播放animation-fill-mode动画执行之前和之后如何给动画的目标应用样式(停留在哪一帧)animation-play-state动画暂停/播放(用于js控制动画object.style.animationPlayState=“paused”,default: running)注:以上顺序是简写顺序@keyframes animationname {keyframes-selector {css-styles;}}值描述animationname动画名称keyframes-selector定义帧css-styles该帧的样式一个可控的动画<button @click=“animationMove”>stopAnimation</button><div class=“animation” ref=“animation”></div>animationMove (): void { let stateg = ‘paused’ if (this.$refs.animation.style.animationPlayState === ‘paused’) { state = ‘running’ } this.$refs.animation.style.animationPlayState = state}.animation { position: absolute; border-radius: 50%; top: 120px; animation: move 1.2s ease-in 80ms infinite alternate;}@keyframes move { 0% { left: 20px; background-color: red; width: 40px; height: 40px; transform: translateY(0) } 100% { left: 400px; background-color: rgb(169, 185, 214); width: 100px; height: 100px; transform: translateY(100px) }}动画和过渡的相同之处应该就是缓冲函数不可变,不同之处就是动画可以定义多个关键帧,过渡只能定义两个关键帧(起始帧和结束帧)动画修改一个元素的 width 和 height 会改变它的形状,而且可能引起页面上其它元素的移动和形状改变,这个过程称为布局。基于 CSS 的动画和原生支持的 Web 动画通常在称为合成器线程的线程上处理,transforms 和 opacity 都可以在合成器线程中处理;它与浏览器的主线程不同,在该主线程中执行样式,布局,绘制和 JavaScript。这意味着如果浏览器在主线程上运行一些耗时的任务,这些动画可以继续运行而不会中断;如果任何动画出发了绘制,布局,或者两者,那么主线程会来完成该工作。这个对基于 CSS 还是 JavaScript 实现的动画都一样,布局或者绘制的开销巨大,让与之关联的 CSS 或 JavaScript 执行工作、渲染都变得毫无意义;避免使用触发布局或绘制的属性动画。对于大多数现代浏览器,这意味着将动画(修改的属性)限制为 opacity 和 transform;参考:How JavaScript works: Under the hood of CSS and JS animations + how to optimize their performanceMDN:transitionMDN:animation ...

December 29, 2018 · 1 min · jiezi

「面试题」如何实现一个圣杯布局?

前言最近,有个朋友向我诉苦说,面试的时候突然被问到了如何实现布局和原理,有点懵。之前JavaScript的部分回答得挺好的,偏偏在这里翻船了,完全没有思路,后面的面试状态一落千丈。结局也如他所料,没有被录取。我给这个朋友做了解答之后,回家整理出此文。希望其他小伙伴面试中,再被问及圣杯布局的时候,可以沉着作答。本文将介绍经典布局——圣杯布局的原理以及两种实现方法:浮动和flex。什么是圣杯布局?圣杯布局是为了讨论「三栏液态布局」的实现,最早的完美实现是由 Matthew Levine 在 2006 年写的一篇文章 《In Search of the Holy Grail》 ,它主要讲述了网页中关于最佳圣杯的实现方法。它有以下几点要求:上部(header)和下部(footer)各自占领屏幕所有宽度。上下部之间的部分(container)是一个三栏布局。三栏布局两侧宽度不变,中间部分自动填充整个区域。中间部分的高度是三栏中最高的区域的高度。接下来,将会介绍两种实现的方法。它们的最终效果都一样,如下图:实现方法1:浮动先上代码:<div class=“header”> <h4>header</h4></div><div class=“container”> <div class=“middle”> <h4>middle</h4> <p>middle-content</p> </div> <div class=“left”> <h4>left</h4> <p>left-content</p> </div> <div class=“right”> <h4>right</h4> <p>right-content</p> </div></div><div class=“footer”> <h4>footer</h4></div>.header, .footer { border: 1px solid #333; background: #ccc; text-align: center;}.footer { clear: both;}.container { padding:0 220px 0 200px; overflow: hidden;}.left, .middle, .right { position: relative; float: left; min-height: 130px;}.middle { width: 100%; background: blue;}.left { margin-left: -100%; left: -200px; width: 200px; background: red;}.right { margin-left: -220px; right: -220px; width: 220px; background: green;}解析一下思路:在html中,先定义好header和footer的样式,使之横向撑满。在container中的三列设为浮动和相对定位(后面会用到),middle要放在最前面,footer清除浮动。三列的左右两列分别定宽200px和220px,中间部分middle设置100%撑满这样因为浮动的关系,middle会占据整个container,左右两块区域被挤下去了接下来设置left的 margin-left:-100%;,让left回到上一行最左侧但这会把middle给遮住了,所以这时给外层的container设置 padding:0 220px 0 200px;,给left空出位置这时left并没有在最左侧,因为之前已经设置过相对定位,所以通过 left:-200px; 把left拉回最左侧同样的,对于right区域,设置 margin-right:-220px; 把right拉回第一行这时右侧空出了220px的空间,所以最后设置 `right:-220px;##把right区域拉到最右侧就行了。实现方法2:flex弹性盒子用弹性盒子来实现圣杯布局特别简单,只需要把中间的部分用flex布局即可。<div class=“header”> <h4>header</h4></div><div class=“container”> <div class=“left”> <h4>left</h4> <p>left-content</p> </div> <div class=“middle”> <h4>middle</h4> <p>middle-content</p> </div> <div class=“right”> <h4>right</h4> <p>right-content</p> </div></div><div class=“footer”> <h4>footer</h4></div>.header, .footer { border: 1px solid #333; background: #ccc; text-align: center;}.container { display: flex;}.left { width: 200px; background: red;}.middle { flex: 1; background: blue;}.right { width: 220px; background: green;}解析一下思路:header和footer同上面一样,横向撑满。footer不用再清浮动了container中的left、middle、right依次排布即可,不用特意将middle放置到最前面给container设置弹性布局 display:flex;left和right区域定宽,middle设置 flex:1; 即可总结总的来说,弹性布局是最适合实现圣杯布局的方法了,相较浮动,弹性布局的结构更清楚,更好理解,也不用担心移动端的适配问题。而浮动的方法,在面试中可能会遇到,主要考察对布局的理解能力。所以,建议大家可以把浮动的例子拷贝下来,自行模拟一把,以便加深理解。参考文献In Search of the Holy GrailPS:欢迎关注我的公众号 “超哥前端小栈”,交流更多的想法与技术。 ...

December 27, 2018 · 1 min · jiezi

前端每日实战:165# 视频演示如何用 Vue 创作一个算术训练程序(内含 3 个视频)

效果预览按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。https://codepen.io/comehope/pen/dwzRyQ可交互视频此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。请用 chrome, safari, edge 打开观看。第 1 部分:https://scrimba.com/p/pEgDAM/ca6wWSk第 2 部分:https://scrimba.com/p/pEgDAM/c7Zy2AZ第 3 部分:https://scrimba.com/p/pEgDAM/c9R2Gsy源代码下载每日前端实战系列的全部源代码请从 github 下载:https://github.com/comehope/front-end-daily-challenges代码解读本项目可以训练加、减、乘、除四则运算。比如训练加法时,界面给出 2 个数值表示 2 个加数,小朋友心算出结果后大声说出,然后点击“?”按钮查看结果,根据对照的结果,如果计算正确(或错误),就点击绿勾(或红叉),然后再开始下一道测验。界面中还会显示已经做过几道题,正确率是多少。为了增强趣味性,加入了音效,答对时会响起小猫甜美的叫声,答错时响起的是小猫失望的叫声。页面用纯 css 布局,程序逻辑用 vue 框架编写,用 howler.js 库播放音效。整个应用分成 4 个步骤实现:静态页面布局、加法的程序逻辑、四则运算的程序逻辑、音效处理。一、页面布局先创建 dom 结构,整个文档分成 4 部分,.choose-type 是一组多选一按钮,用于选择四则运算的类型,.score 是成绩统计数据,.expression 是一个算式,它也是游戏的主体部分,.judgment 用于判断答题是否正确:<div id=“app”> <div class=“choose-type”></div> <div class=“score”></div> <div class=“expression”></div> <div class=“judgment”></div></div>.choose-type 一共包含 4 个 input[type=radio] 控件,命名为 arithmetic-type,加、减、乘、除 4 种运算类型的值分别为 1、2、3、4,每个控件后跟随一个对应的label,最终我们将把 input 控件隐藏起来,而让用户操作 label。<div id=“app”> <div class=“choose-type”> <div class=“choose-type”> <input type=“radio” id=“addition” name=“arithmetic-type” value=“1”> <label for=“addition”>addition</label> <input type=“radio” id=“subtraction” name=“arithmetic-type” value=“2”> <label for=“subtraction”>subtraction</label> <input type=“radio” id=“multiplication” name=“arithmetic-type” value=“3”> <label for=“multiplication”>multiplication</label> <input type=“radio” id=“division” name=“arithmetic-type” value=“4”> <label for=“division”>division</label> </div> <!– 略 –></div>.score 包含 2 个数据,一个是已经做过的题目数,一个是正确率:<div id=“app”> <!– 略 –> <div class=“score”> <span>ROUND 15</span> <span>SCORE 88%</span> </div> <!– 略 –></div>.expression 把一个表达式的各部分拆开,以便能修饰表达式各部分的样式。.number 表示等式左边的 2 个运算数,.operation 表示运算符和等号,.show 是一个问号,同时它也是一个按钮,当心算出结果后,点击它,就显示出 .result 元素,展示运算结果:<div id=“app”> <!– 略 –> <div class=“expression”> <span class=“number”>10</span> <span class=“operation”>+</span> <span class=“number”>20</span> <span class=“operation”>=</span> <span class=“button show”>?</span> <span class=“result”>30</span> </div> <!– 略 –></div>.judgment 包含 2 个按钮,分别是表示正确的绿勾和表示错误的红叉,显示在结果的下方:<div id=“app”> <!– 略 –> <div class=“judgment”> <span class=“button right”>✔</span> <span class=“button wrong”>✘</span> </div></div>至此,完整的 dom 结构如下:<div id=“app”> <div class=“choose-type”> <input type=“radio” id=“addition” name=“arithmetic-type” value=“1”> <label for=“addition”>addition</label> <input type=“radio” id=“subtraction” name=“arithmetic-type” value=“2”> <label for=“subtraction”>subtraction</label> <input type=“radio” id=“multiplication” name=“arithmetic-type” value=“3”> <label for=“multiplication”>multiplication</label> <input type=“radio” id=“division” name=“arithmetic-type” value=“4”> <label for=“division”>division</label> </div> <div class=“score”> <span>ROUND 15</span> <span>SCORE 88%</span> </div> <div class=“expression”> <span class=“number”>10</span> <span class=“operation”>+</span> <span class=“number”>20</span> <span class=“operation”>=</span> <span class=“button show”>?</span> <span class=“result”>30</span> </div> <div class=“judgment”> <span class=“button right”>✔</span> <span class=“button wrong”>✘</span> </div></div>接下来用 css 布局。居中显示:body{ margin: 0; height: 100vh; display: flex; align-items: center; justify-content: center; background: linear-gradient(lightyellow, tan);}设置应用的容器样式,黑色渐变背景,子元素纵向排列,尺寸用相对单位 vw 和 em,以便在窗口缩放后能自适应新窗口尺寸:#app { width: 66vmin; display: flex; flex-direction: column; align-items: center; box-shadow: 0 1em 4em rgba(0, 0, 0, 0.5); border-radius: 2em; padding: 8em 5em; background: linear-gradient(black, dimgray, black); font-family: sans-serif; font-size: 1vw; user-select: none;}布局 .choose-type 区域。隐藏 input 控件,设置 label 为天蓝色:.choose-type input[name=arithmetic-type] { position: absolute; visibility: hidden;}.choose-type label { font-size: 2.5em; color: skyblue; margin: 0.3em; letter-spacing: 0.02em;}在 label 之间加入分隔线:.choose-type label { position: relative;}.choose-type label:not(:first-of-type)::before { content: ‘|’; position: absolute; color: skyblue; left: -0.5em; filter: opacity(0.6);}设置 label 在鼠标悬停时变色,当 input 控件被选中时对应的 label 会变色、首字母变大写并显示下划线,为了使视觉效果切换平滑,设置了缓动时间。这里没有使用 text-decoration: underline 设置下划线,是因为用 border 才有缓动效果:.choose-type label { transition: 0.3s;}.choose-type label:hover { color: deepskyblue; cursor: pointer;}.choose-type input[name=arithmetic-type]:checked + label { text-transform: capitalize; color: deepskyblue; border-style: solid; border-width: 0 0 0.1em 0;}.score 区域用银色字,2 组数据之间留出一些间隔:.score{ font-size: 2em; color: silver; margin: 1em 0 2em 0; width: 45%; display: flex; justify-content: space-between;}.expression 区域用大字号,各元素用不同的颜色区分:.expression { font-size: 12em; display: flex; align-items: center;}.expression span { margin: 0 0.05em;}.expression .number{ color: orange;}.expression .operation{ color: skyblue;}.expression .result{ color: gold;}.show 是等号右边的问号,它同时也是一个按钮,在这里把按钮的样式 .button 独立出来,因为后面还会用到 .button 样式:.expression .show { color: skyblue; font-size: 0.8em; line-height: 1em; width: 1.5em; text-align: center;}.button { background-color: #222; border: 1px solid #555; padding: 0.1em;}.button:hover { background-color: #333; cursor: pointer;}.button:active { background-color: #222;}设置 .judgment 区域 2 个按钮的样式,它们还共享了 .button 样式:.judgment { font-size: 8em; align-self: flex-end;}.judgment .wrong { color: orangered;}.judgment .right { color: lightgreen;}至此,静态页面布局完成,完整的 css 代码如下:body{ margin: 0; height: 100vh; display: flex; align-items: center; justify-content: center; background: linear-gradient(lightyellow, tan);}#app { width: 66vw; display: flex; flex-direction: column; align-items: center; box-shadow: 0 1em 4em rgba(0, 0, 0, 0.5); border-radius: 2em; padding: 8em 5em; background: linear-gradient(black, dimgray, black); font-family: sans-serif; font-size: 1vw; user-select: none;}.choose-type input[name=arithmetic-type] { position: absolute; visibility: hidden;}.choose-type label { font-size: 2.5em; color: skyblue; margin: 0.3em; letter-spacing: 0.02em; position: relative; transition: 0.3s;}.choose-type label:not(:first-of-type)::before { content: ‘|’; position: absolute; color: skyblue; left: -0.5em; filter: opacity(0.6);}.choose-type label:hover { color: deepskyblue; cursor: pointer;}.choose-type input[name=arithmetic-type]:checked + label { text-transform: capitalize; color: deepskyblue; border-style: solid; border-width: 0 0 0.1em 0;}.score{ font-size: 2em; color: silver; margin: 1em 0 2em 0; width: 45%; display: flex; justify-content: space-between;}.expression { font-size: 12em; display: flex; align-items: center;}.expression span { margin: 0 0.05em;}.expression .number{ color: orange;}.expression .operation{ color: skyblue;}.expression .result{ color: gold;}.expression .show { color: skyblue; font-size: 0.8em; line-height: 1em; width: 1.5em; text-align: center;}.judgment { font-size: 8em; align-self: flex-end;}.judgment .wrong { color: orangered;}.judgment .right { color: lightgreen;}.button { background-color: #222; border: 1px solid #555; padding: 0.1em;}.button:hover { background-color: #333; cursor: pointer;}.button:active { background-color: #222;}二、加法的程序逻辑我们先用加法把流程跑通,再把加法扩展为四则运算。引入 vue 框架:<script src=“https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.min.js"></script>创建一个 Vue 对象:let vm = new Vue({ el: ‘#app’,})定义数据,round 存储题目数,round.all 表示总共答过了多少道题,round.right 表示答对了多少道题;numbers 数组包含 2 个元素,用于存储等式左边的 2 个运算数,用数组是为了便于后面使用解构语法:let vm = new Vue({ ///…略 data: { round: {all: 0, right: 0}, numbers: [0, 0], } ///…略})定义计算属性,operation 是操作符,目前是加号,result 是计算结果,等于 2 个运算数相加,score 是正确率,开始做第一题时正确率显示为 100%,后续根据实际答对的题数计算正确率:let vm = new Vue({ ///…略 computed: { operation: function() { return ‘+’ }, result: function() { return this.numbers[0] + this.numbers[1] }, score: function() { return this.round.all == 1 ? 100 : Math.round(this.round.right / (this.round.all - 1) * 100) } }, ///…略})把数据绑定到 html 模板中:<div id=“app”> <!– 略 –> <div class=“score”> <span>ROUND {{round.all - 1}}</span> <span>SCORE {{score}}%</span> </div> <div class=“expression”> <span class=“number”>{{numbers[0]}}</span> <span class=“operation”>{{operation}}</span> <span class=“number”>{{numbers[1]}}</span> <span class=“operation”>=</span> <span class=“button show”>?</span> <span class=“result”>{{result}}</span> </div> <!– 略 –></div>至此,页面中的数据都是动态获取的了。等式右边的问号和结果不应同时显示出来,在用户思考时应显示问号,思考结束后应隐藏问号显示结果。为此,增加一个 isThinking 变量,用于标志用户所处的状态,默认为 true,即进入游戏时,用户开始思考第 1 道题目:let vm = new Vue({ ///…略 data: { round: {all: 0, right: 0}, numbers: [0, 0], isThinking: true, }, ///…略})把 isThinking 绑定到 html 模板中,用户思考时只显示问号 .show,否则显示结果 .result 和判断结果正确与否的按钮 .judgment,此处请注意,对于占据同一个视觉位置的元素,用 v-show=false,即 display: none 隐藏,对于占据独立视觉位置的元素,用 visibility: hidden 隐藏:<div id=“app”> <!– 略 –> <div class=“expression”> <!– 略 –> <span class=“button show” v-show=“isThinking”>?</span> <span class=“result” v-show="!isThinking”>{{result}}</span> </div> <div class=“judgment” :style="{visibility: isThinking ? ‘hidden’ : ‘visible’}"> <!– 略 –> </div></div>接下来生成随机运算数。创建一个 next() 方法用于开始下一个题目,那么在页面载入后就应执行这个方法初始化第 1 道题目:let vm = new Vue({ ///…略 methods: { next: function() { }, },})window.onload = vm.nextnext() 方法一方面要负责初始化运算数,还要把答过的题目数加1,这里独立出来一个 newRound() 方法是为了方便后面复用它:let vm = new Vue({ ///…略 methods: { newRound: function() { this.numbers = this.getNumbers() this.isThinking = true }, next: function() { this.newRound() this.round.all++ }, },})getNumbers() 方法用于生成 2 个随机数,它调用 getRandomNumber() 方法来生成一个随机数,其中 level 参数表示随机数的取值范围,level 为 1 时,生成的随机数介于 1 ~ 9 之间,level 为 2 时,生成的随机数介于 10 ~ 99 之间。为了增加一点加法的难度,我们把 level 设置为 2:let vm = new Vue({ ///…略 methods: { getRandomNumber: function(level) { let min = Math.pow(10, level - 1) let max = Math.pow(10, level) return min + Math.floor(Math.random() * (max - min)) }, getNumbers: function() { let level = 2 let a = this.getRandomNumber(level) let b = this.getRandomNumber(level) return [a, b] }, newRound: function() { this.numbers = this.getNumbers() this.isThinking = true }, next: function() { this.newRound() this.round.all++ }, },})此时,每刷新一次页面,运算数就会跟着刷新,因为每次页面加载都会运行 vm.next() 方法生成新的随机数。接下来我们来处理按钮事件,页面中一共有 3 个按钮:问号按钮 .show 被点击后应显示结果;绿勾按钮 .right 被点击后应给答对题的数目加 1,然后进入下一道题;红叉按钮 .wrong 被点击后直接进入下一道题,所以我们在程序中增加 3 个方法,getResult()、answerRight()、answerWrong 分别对应上面的 3 个点击事件:let vm = new Vue({ ///…略 methods: { ///…略 getResult: function() { this.isThinking = false }, answerRight: function() { this.round.right++ this.next() }, answerWrong: function() { this.next() }, },})把事件绑定到 html 模板:<div id=“app”> <!– 略 –> <div class=“expression”> <!– 略 –> <span class=“button show” v-show=“isThinking” @click=“getResult”>?</span> <!– 略 –> </div> <div class=“judgment” :style="{visibility: isThinking ? ‘hidden’ : ‘visible’}"> <span class=“button right” @click=“answerRight”>✔</span> <span class=“button wrong” @click=“answerWrong”>✘</span> </div></div>至此,加法程序就全部完成了,可以一道又一道题一直做下去。此时的 html 代码如下:<div id=“app”> <div class=“choose-type”> <!– 没有改变 –> </div> <div class=“score”> <span>ROUND {{round.all - 1}}</span> <span>SCORE {{score}}%</span> </div> <div class=“expression”> <span class=“number”>{{numbers[0]}}</span> <span class=“operation”>{{operation}}</span> <span class=“number”>{{numbers[1]}}</span> <span class=“operation”>=</span> <span class=“button show” v-show=“isThinking” @click=“getResult”>?</span> <span class=“result” v-show="!isThinking">{{result}}</span> </div> <div class=“judgment” :style="{visibility: isThinking ? ‘hidden’ : ‘visible’}"> <span class=“button right” @click=“answerRight”>✔</span> <span class=“button wrong” @click=“answerWrong”>✘</span> </div></div>此时的 javascript 代码如下:let vm = new Vue({ el: ‘#app’, data: { round: {all: 0, right: 0}, numbers: [0, 0], isThinking: true, }, computed: { operation: function() { return ‘+’ }, result: function() { return this.numbers[0] + this.numbers[1] }, score: function() { return this.round.all == 1 ? 100 : Math.round(this.round.right / (this.round.all - 1) * 100) } }, methods: { getRandomNumber: function(level) { let min = Math.pow(10, level - 1) let max = Math.pow(10, level) return min + Math.floor(Math.random() * (max - min)) }, getNumbers: function() { let level = 2 let a = this.getRandomNumber(level) let b = this.getRandomNumber(level) return [a, b] }, newRound: function() { this.numbers = this.getNumbers() this.isThinking = true }, next: function() { this.newRound() this.round.all++ }, getResult: function() { this.isThinking = false }, answerRight: function() { this.round.right++ this.next() }, answerWrong: function() { this.next() }, },})window.onload = vm.next三、四则运算的程序逻辑我们先来评估一下四种运算在这个程序里会在哪些方面有差异。首先,运算符不同,加、减、乘、除的运算符分别是“+”、“-”、“×”、“÷”;第二是运算函数不同,这个不用多说。根据这 2 点,我们定义一个枚举对象 ARITHMETIC_TYPE,用它存储四种运算的差异,每个枚举对象有 2 个属性,operation 代表操作符,f() 函数是运算逻辑。另外,我们再声明一个变量 arithmeticType,用于存储用户当前选择的运算类型:let vm = new Vue({ ///…略 data: { ///…略 ARITHMETIC_TYPE: { ADDITION: 1, SUBTRACTION: 2, MULTIPLICATION: 3, DIVISION: 4, properties: { 1: {operation: ‘+’, f: ([x, y]) => x + y}, 2: {operation: ‘-’, f: ([x, y]) => x - y}, 3: {operation: ‘×’, f: ([x, y]) => x * y}, 4: {operation: ‘÷’, f: ([x, y]) => x / y} } }, arithmeticType: 1, },})改造计算属性中关于运算符和计算结果的函数:let vm = new Vue({ ///…略 computed: { ///…略 operation: function() { // return ‘+’ return this.ARITHMETIC_TYPE.properties[this.arithmeticType].operation }, result: function() { // return this.numbers[0] + this.numbers[1] return this.ARITHMETIC_TYPE.properties[this.arithmeticType].f(this.numbers) }, ///…略 },})因为上面 2 个计算属性都用到了 arithmeticType 变量,所以当用户选择运算类型时,这 2 个计算属性的值会自动更新。另外,为了让 ui 逻辑更严密,我们令 arithmeticType 的值改变时,开始一个新题目:let vm = new Vue({ ///…略 watch: { arithmeticType: function() { this.newRound() } }})然后,把 arithmeticType 变量绑定到 html 模板中的 input 控件上:<div id=“app”> <div class=“choose-type”> <input type=“radio” id=“addition” name=“arithmetic-type” value=“1” v-model=“arithmeticType”> <label for=“addition”>addition</label> <input type=“radio” id=“subtraction” name=“arithmetic-type” value=“2” v-model=“arithmeticType”> <label for=“subtraction”>subtraction</label> <input type=“radio” id=“multiplication” name=“arithmetic-type” value=“3” v-model=“arithmeticType”> <label for=“multiplication”>multiplication</label> <input type=“radio” id=“division” name=“arithmetic-type” value=“4” v-model=“arithmeticType”> <label for=“division”>division</label> </div> <!– 略 –></div>至此,当选择不同的运算类型时,表达式的运算符和计算结果都会自动更新为匹配的值,比如选择乘法时,运算符就变为乘号,运算结果为 2 个运算数的乘积。不过,此时的最明显的问题是,除法的运算数因为是随机生成的,商经常是无限小数,为了更合理,我们规定这里的除法只做整除运算。再延伸一下,对于减法,为了避免差为负数,也规定被减数不小于减数。解决这个问题的办法是在 ARITHMETIC_TYPE 枚举中添加一个 gen() 函数,用于存储生成运算数的逻辑,gen() 函数接收一个包含 2 个随机数的数组作为参数,对于加法和乘法,直接返回数组本身,减法的 gen() 函数为 gen: ([a, b]) => a >= b ? [a, b] : [b, a],除法的 gen() 函数为 gen: ([a, b]) => [a * b, b],经过如此处理的运算数,就可以实现上面规定的逻辑了。改造后的 ARITHMETIC_TYPE 如下:let vm = new Vue({ ///…略 data: { ///…略 ARITHMETIC_TYPE: { ADDITION: 1, SUBTRACTION: 2, MULTIPLICATION: 3, DIVISION: 4, pproperties: { 1: {operation: ‘+’, f: (arr) => arr, gen: ([a, b]) => [a, b]}, 2: {operation: ‘-’, f: ([x, y]) => x - y, gen: ([a, b]) => a >= b ? [a, b] : [b, a]}, 3: {operation: ‘×’, f: (arr) => arr, gen: ([a, b]) => [a, b]}, 4: {operation: ‘÷’, f: ([x, y]) => x / y, gen: ([a, b]) => [a * b, b]} } }, ///…略 }, ///…略})然后,在 getNumbers() 中调用 gen() 方法:let vm = new Vue({ ///…略 methods: { ///…略 getNumbers: function() { let level = 2 let a = this.getRandomNumber(2) let b = this.getRandomNumber(2) // return [a, b] return this.ARITHMETIC_TYPE.properties[this.arithmeticType].gen([a, b]) }, ///…略 }, ///…略})至此,减法可以保证差不为负数,除法也可以保证商是整数了。接下来,我们来配置训练难度。对大多数人来说,2 个二位数的加减法不是很难,但是 2 个二位数的乘除法的难度就大多了。在生成随机数时,因为定义了 level=2,所以取值范围固定是 11 ~ 99,我们希望能够灵活配置每个运算数的取值范围,为此,我们需要再为 ARITHMETIC_TYPE 枚举中增加一个 level 属性,用于表示随机数的取值范围,它是一个包含 2 个元素的数组,分别表示 2 个运算数的取值范围,改造后的 ARITHMETIC_TYPE 如下:let vm = new Vue({ ///…略 data: { ///…略 ARITHMETIC_TYPE: { ADDITION: 1, SUBTRACTION: 2, MULTIPLICATION: 3, DIVISION: 4, properties: { 1: {operation: ‘+’, f: ([x, y]) => x + y, gen: (arr) => arr, level: [3, 2]}, 2: {operation: ‘-’, f: ([x, y]) => x - y, gen: ([a, b]) => a >= b ? [a, b] : [b, a], level: [3, 2]}, 3: {operation: ‘×’, f: ([x, y]) => x * y, gen: (arr) => arr, level: [2, 1]}, 4: {operation: ‘÷’, f: ([x, y]) => x / y, gen: ([a, b]) => [a * b, b], level: [2, 1]} } }, ///…略 }, ///…略})然后,把 getNumbers() 函数的 level 变量的值改为从枚举 ARITHMETIC_TYPE 中取值:let vm = new Vue({ ///…略 methods: { getNumbers: function() { let level = this.ARITHMETIC_TYPE.properties[this.arithmeticType].level let a = this.getRandomNumber(level[0]) let b = this.getRandomNumber(level[1]) return this.ARITHMETIC_TYPE.properties[this.arithmeticType].gen([a, b]) }, ///…略 }, ///…略})现在运行程序可以看到,加减法的 2 个运算数分别是 3 位数和 2 位数,而乘除法的 2 个运算数则分别是 2 位数和 1 位数,你也可以根据自己的需要来调整训练难度。至此,四则运算的程序逻辑全部完成,此时的 javascript 代码如下:let vm = new Vue({ el: ‘#app’, data: { round: {all: 0, right: 0}, numbers: [0, 0], isThinking: true, ARITHMETIC_TYPE: { ADDITION: 1, SUBTRACTION: 2, MULTIPLICATION: 3, DIVISION: 4, properties: { 1: {operation: ‘+’, f: ([x, y]) => x + y, gen: (arr) => arr, level: 2}, 2: {operation: ‘-’, f: ([x, y]) => x - y, gen: ([a, b]) => a >= b ? [a, b] : [b, a], level: 2}, 3: {operation: ‘×’, f: ([x, y]) => x * y, gen: (arr) => arr, level: 1}, 4: {operation: ‘÷’, f: ([x, y]) => x / y, gen: ([a, b]) => [a * b, b], level: 1} } }, arithmeticType: 1, }, computed: { operation: function() { return this.ARITHMETIC_TYPE.properties[this.arithmeticType].operation }, result: function() { return this.ARITHMETIC_TYPE.properties[this.arithmeticType].f(this.numbers) }, score: function() { return this.round.all == 1 ? 100 : Math.round(this.round.right / (this.round.all - 1) * 100) } }, methods: { getRandomNumber: function(level) { let min = Math.pow(10, level - 1) let max = Math.pow(10, level) return min + Math.floor(Math.random() * (max - min)) }, getNumbers: function() { let level = this.ARITHMETIC_TYPE.properties[this.arithmeticType].level let a = this.getRandomNumber(level[0]) let b = this.getRandomNumber(level[1]) return this.ARITHMETIC_TYPE.properties[this.arithmeticType].gen([a, b]) }, newRound: function() { this.numbers = this.getNumbers() this.isThinking = true }, next: function() { this.newRound() this.round.all++ }, getResult: function() { this.isThinking = false }, answerRight: function() { this.round.right++ this.next() }, answerWrong: function() { this.next() }, }, watch: { arithmeticType: function() { this.newRound() } }})window.onload = vm.next四、音效处理引入 howler 库:<script src=“https://cdnjs.cloudflare.com/ajax/libs/howler/2.1.1/howler.min.js"></script>声明变量 sound,它有 2 个属性 right 和 wrong,分别代表回答正确和错误时的音效,属性值是一个 Howl 对象,在构造函数中指定音频文件的 url:let vm = new Vue({ ///…略 data: { ///…略 sound: { right: new Howl({src: [‘https://freesound.org/data/previews/203/203121_777645-lq.mp3’]}), wrong: new Howl({src: [‘https://freesound.org/data/previews/415/415209_5121236-lq.mp3’]}) }, }, ///…略})在 answerRight() 方法和 answerWrong() 方法中分别调用播放声音的 play() 方法即可:let vm = new Vue({ ///…略 methods: { ///…略 answerRight: function() { this.round.right++ this.sound.right.play() this.next() }, answerWrong: function() { this.sound.wrong.play() this.next() }, ///…略})现在,当点击绿勾时,就会响起小猫甜美的叫声;当点击红叉时,响起的是小猫失望的叫声。至此,程序全部开发完成,最终的 javascript 代码如下:let vm = new Vue({ el: ‘#app’, data: { round: {all: 0, right: 0}, numbers: [0, 0], isThinking: true, ARITHMETIC_TYPE: { ADDITION: 1, SUBTRACTION: 2, MULTIPLICATION: 3, DIVISION: 4, properties: { 1: {operation: ‘+’, f: ([x, y]) => x + y, gen: (arr) => arr, level: [3, 2]}, 2: {operation: ‘-’, f: ([x, y]) => x - y, gen: ([a, b]) => a >= b ? [a, b] : [b, a], level: [3, 2]}, 3: {operation: ‘×’, f: ([x, y]) => x * y, gen: (arr) => arr, level: [2, 1]}, 4: {operation: ‘÷’, f: ([x, y]) => x / y, gen: ([a, b]) => [a * b, b], level: [2, 1]} } }, arithmeticType: 1, sound: { right: new Howl({src: [‘https://freesound.org/data/previews/203/203121_777645-lq.mp3’]}), wrong: new Howl({src: [‘https://freesound.org/data/previews/415/415209_5121236-lq.mp3’]}) }, }, computed: { operation: function() { return this.ARITHMETIC_TYPE.properties[this.arithmeticType].operation }, result: function() { return this.ARITHMETIC_TYPE.properties[this.arithmeticType].f(this.numbers) }, score: function() { return this.round.all == 1 ? 100 : Math.round(this.round.right / (this.round.all - 1) * 100) } }, methods: { getRandomNumber: function(level) { let min = Math.pow(10, level - 1) let max = Math.pow(10, level) return min + Math.floor(Math.random() * (max - min)) }, getNumbers: function() { let level = this.ARITHMETIC_TYPE.properties[this.arithmeticType].level let a = this.getRandomNumber(level[0]) let b = this.getRandomNumber(level[1]) return this.ARITHMETIC_TYPE.properties[this.arithmeticType].gen([a, b]) }, newRound: function() { this.numbers = this.getNumbers() this.isThinking = true }, next: function() { this.newRound() this.round.all++ }, getResult: function() { this.isThinking = false }, answerRight: function() { this.round.right++ this.sound.right.play() this.next() }, answerWrong: function() { this.sound.wrong.play() this.next() }, }, watch: { arithmeticType: function() { this.newRound() } }})window.onload = vm.next大功告成! ...

December 27, 2018 · 11 min · jiezi

前端 CSS : 4# CSS 实现暖暖的小火堆

介绍原文链接感謝 comehope 大佬的 [前端每日实战]效果预览github.io 浏览源代码地址https://github.com/shanyuhai1…代码解读1. 首先是完成 html 结构<figure class=“container”> <section class=“stars”></section> <section class=“fires”></section></figure>常规样式初始化* { margin: 0; padding: 0;}body { margin: 0; padding: 0 0.5vw; height: 100vh; background-color: #333; overflow: hidden;}.container { display: flex; flex-direction: column; align-items: center; justify-content: center;}2. 天上的星星原文中星星是固定位置并且不会闪烁的而这里我们将会改变这一状态, 而且为了避免重复手动给星星固定位置及大小, 所以采用了 d3 库来减少麻烦<script src=“https://d3js.org/d3.v5.min.js"></script>首先将 .stars 改为 grid 布局 使用 span 标签 作为星星因为星星要分时间闪烁所以随机一个 –delay 参数// index.jsconst COLUMNS = 15;d3.select(’.stars’) .style(’–columns’, COLUMNS) .selectAll(‘span’) .data(d3.range(COLUMNS * COLUMNS)) .enter() .append(‘span’) .style(’–delay’, () => Math.random() * 20);先给出大概的范围, 查看下边界.stars { width: 99vw; height: 70vh; position: absolute; display: grid; grid-template-columns: repeat(var(–columns), 1fr); border: 1px solid;}.stars span { width: 0.6vw; height: 0.6vw; color: whitesmoke; background-color: currentColor;}星星现在只是一个个正方形, 再给正方形添加上旋转闪现的动画即可.stars span { transform: scale(0); animation: spin 20s linear infinite; animation-delay: calc(var(–delay) * 1s);}@keyframes spin { 0% { transform: rotate(0deg) scale(1); } 5%, 15% { transform: rotate(90deg) scale(0); background: goldenrod; } 17.5% { transform: rotate(180deg) scale(1); background-color: currentColor; } 20%, 100% { transform: rotate(90deg) scale(0); }}3. 添加火堆首先是修改 DOM<section class=“fires”> <span class=“fires__flame”></span> <span class=“fires__flame”></span> <span class=“fires__flame”></span> <span class=“fires__flame”></span> <span class=“fires__stick”></span> <span class=“fires__stick”></span></section>使火堆居中, 利用媒体查询改变一下在手机端偏小的问题.fires { position: relative; border: 1px solid;}@media screen and (min-width: 451px) { .fires { width: 15vw; height: 15vw; margin-top: -7vw; }}@media screen and (max-width: 450px) { .fires { width: 18vh; height: 18vh; margin-top: -5vw; }}接着完成火焰效果, 在父级添加可用的 color , border-radius 变量.fires { position: relative; display: flex; align-items: center; justify-content: center; border: 1px solid; –color-one: #D92B29; –color-two: #F5732A; –color-three: #F2B338; –color-four: #F5D549; –shape-one: 79% 21% 64% 36% / 43% 61% 39% 57%; –shape-two: 23% 77% 66% 34% / 57% 72% 28% 43%; –shape-three: 78% 22% 63% 37% / 39% 27% 73% 61%; –shape-four: 35% 65% 78% 22% / 54% 50% 50% 46%;}@media screen and (min-width: 451px) { .fires__flame { width: 6vw; } .fires__flame:nth-of-type(1) { height: 15vw; } .fires__flame:nth-of-type(2) { height: 12vw; transform: translate(2.25vw, 1.2vw) rotate(30deg); } .fires__flame:nth-of-type(3) { height: 13.5vw; transform: translate(-2.25vw, 1.2vw) rotate(-30deg); } .fires__flame:nth-of-type(4) { height: 10.5vw; }}@media screen and (max-width: 450px) { .fires__flame { width: 7.2vh; } .fires__flame:nth-of-type(1) { height: 18vh; } .fires__flame:nth-of-type(2) { height: 14.4vh; transform: translate(2.7vh, 1.44vh) rotate(30deg); } .fires__flame:nth-of-type(3) { height: 16.2vh; transform: translate(-2.7vh, 1.44vh) rotate(-30deg); } .fires__flame:nth-of-type(4) { height: 12.6vh; }}.fires__flame { position: absolute; background-color: var(–color-one); border-radius: var(–shape-one); z-index: 0; animation-name: fire; animation-duration: 1.5s; animation-iteration-count: infinite; transition: ease 0.4s;}.fires__flame:nth-of-type(2) { border-radius: var(–shape-two); background-color: var(–color-two); opacity: 0.9; z-index: 2; animation-delay: 0.2s;}.fires__flame:nth-of-type(3) { border-radius: var(–shape-three); background-color: var(–color-three); opacity: 0.8; z-index: 1; animation-delay: 0.4s;}.fires__flame:nth-of-type(4) { border-radius: var(–shape-four); background-color: var(–color-four); opacity: 0.8; z-index: 1; animation-delay: 0.6s;}当然别忘了火焰的动画效果@keyframes fire { 0% { border-radius: var(–shape-one); background-color: var(–color-one); } 25% { border-radius: var(–shape-two); background-color: var(–color-two); } 50% { border-radius: var(–shape-three); background-color: var(–color-three); } 75% { border-radius: var(–shape-four); background-color: var(–color-four); } 100% { border-radius: var(–shape-one); background-color: var(–color-one); }}再添加木柴@media screen and (min-width: 451px) { .fires__stick { border-radius: 1.5vw; width: 3vw; height: 13.5vw; bottom: -7.5vw; }}@media screen and (max-width: 450px) { .fires__stick { border-radius: 1.8vh; width: 3.6vh; height: 16.2vh; bottom: -9vh; }}.fires__stick { background-color: #5a3600; position: absolute; z-index: 2; transform:rotate(-70deg);}.fires__stick:last-of-type { transform:rotate(70deg); background-color: #4e2f01;}4. 最后最后记得把之前确认位置及大小的 border 边框删除即可 ...

December 25, 2018 · 3 min · jiezi

10分钟入门 CSS3 Animation

简介Animation可以让你不用依赖javascript或jquery,用纯CSS在网页中轻松实现各种动画效果。兼容性animation在绝大部分主流浏览器都得到了很好的支持!还在兼容IE9的同学要谨慎使用。CSS 坐标系在了解animtion之前,我们有必要先了解css的坐标系,因为很多的animation效果都和元素的坐标密切相关。在css3中网页不再是一个二维平面,而是一个三维空间,水平方向、竖直方向和垂直屏幕方向分别对应三维坐标系的x,y,z轴,如下图所示。箭头方向为正向,反之为负向(注意y轴方向与常规笛卡尔坐标系相反)。Animations1. Transformstransform中文译为“转换”,但我更倾向于称呼它“变形”(大名鼎鼎的变形金刚叫transformer)。我们可以使用transform function使html元素产生各种各样的变形,比如平移、缩放、旋转、扭曲等,而且不会影响正常的文档流(document flow)。(1) TranslateTranslate一般译为“翻译”,但在css里面一般用作“平移”,因为translate用于改变html元素的在3d坐标系位置。translate支持在坐标系内任意方向移动(通过任意组合x、y、z方向的向量),单位可以是长度单位和百分比(百分比是相对于被平移的元素自身尺寸,x轴是相对于width,y轴是相对于height,而在z轴方向由于元素是没有‘厚度’的,所以对于z方向不能用百分比表示),例如:transform: translateX(100px) translateY(50%) translateZ(-100px);// 或者简写transform: translate3d(100px, 50%, 2em);注意:translate是xy平面内的2维平移,translate3d是xyz空间内的三维平移;translate也可以单独书写,如 translate: 50% 105px 5rem;,但是该特性尚在实验阶段,很多浏览器不支持,所以现阶段还是配合transform使用。详情参考 MDN translate。(2) ScaleScale意为“缩放”,顾名思义,是用于改变元素的大小。例如:transform: scaleX(2) scaleY(0.5) scaleZ(1);// 或者简写transform: scale3d(2, 0.5, 1);scale方法接收任意数字(正负整数、小数、0)作为参数,该参数为缩放系数,系数>1 效果为放大,0<系数<1 效果为缩小,系数=0 元素尺寸变为无限小而不可见,系数<0 效果为 >0 时的镜像(具体效果可自己实验)。与translate类似,scale也有2维 scale() 和三维 scale3d()之分,也可以单独书写,此处不赘述。(3) RotateRotate意为“旋转”,支持将元素以x、y、z为轴旋转,旋转正方向为面朝坐标轴正向逆时针方向,可参考上面坐标系示意图。rotate方法接收一个角度作为参数,角度>0 正向旋转,角度<0 负向旋转,例如:// 默认绕z轴旋转transform: rotate(90deg);transform: rotateX(30deg) rotateY(60deg) rotateZ(-90deg);与translate和scale不同,rotate不能简写为transform: rotate3d(30deg, 60deg, 90deg)的形式,rotate3d的用法为:前三个参数为数字,代表x、y、z方向的向量,相加得到向量v,第四个参数为角度,表示以向量v为轴逆时针旋转的角度,语法如下:transform: rotate3d(1, 2, 3, 90deg);与translate和scale类似,rotate也可以作为单独的css属性,但还在实验阶段。出于篇幅考虑,我只列出三种最常用的tranform function,剩下的transform function请参考 MDN transform function。(4) 组合我们可以将不同的transform方法组合起来使用,如:transform: translateY(200px) rotateZ(90deg) scale(3);组合方法的执行顺序是从右往左,即先执行scale,然后rotate,最后translate,产生的效果是逐次累加的。方法书写的顺序对最后效果有很大的影响,看下面例子,沿y轴平移和放大,顺序不同,产生的结果有明显差别:如果先translate再scale,平移的距离也将被等比例缩放,而先scale再translate则不会。所以在使用transform需按照 translate -> rotate -> scale 的顺序书写,让scale先执行,以免放大translate的效果,而rotate先translate执行以防止带着平移的距离一起转动。我觉得这是transform目前不方便的地方,因为方法之间相互干扰并不容易理解,书写顺序也不容易记住。在未来有望通过使用独立的css transform属性解决这一问题,因为独立的transform属性对书写顺序没有依赖,方法之间彼此不会干扰。TransitionTransition翻译为“过渡”,强调的是过程。在css中指在一段时间内,元素从一个状态(对应一个css属性)过渡到另一个状态的动态过程。我们可以决定以何种方式过渡过渡和花费多少时间。例如,我们把鼠标悬浮到云上面的时候使其变大一些可以这么写:.cloud{ width: 240px; transition: 1s;}.cloud:hover{ width: 320px;}效果:transition可以和transform结合使用,比如我们可以让云变大的同时转一圈:.cloud:hover{ width: 320px; transform: rotate(360deg);}效果:我们可以给不同的效果设置不同的过渡时间:.cloud{ width: 240px; transition: width 1s, transform 0.5s;}我们也可以给效果设置延时时间,比如我们等宽度增大之后再旋转:.cloud{ width: 240px; transition: width 1s, transform 0.5s 1s;}效果:我们还可以给每个效果设置不同的timing function,用于控制加速效果。比如我们可以让旋转的速度逐渐加快:.cloud{ width: 240px; transition: transform 2s ease-in;}.cloud:hover{ transform: rotate(1080deg);}效果:更多的timing function请后面会进一步讨论,也可以参考 MDN transition-timing-functionsKeyframes(1) 基本用法Keyframe中文译为“关键帧”,是animation中很强大的功能,通俗说就是我们可以通过定义一段动画中的关键点、关键状态来创建动画。Keyframes相比transition对动画过程和细节有更强的控制。我们先看一个例子(部分代码省略)html:<div class=“sky”></div><div class=“grass”></div><div class=“road”> <div class=“lines”></div> <img src=“http://lc-jOYHMCEn.cn-n1.lcfile.com/a3f630c957a4b32d0221.png" class=“mario animated”></div>css:.mario{ position: absolute; left: 0px; width: 100px;}.animated{ animation-name: drive; animation-duration: 1s; animation-timing-function: linear;}@keyframes drive { from{ transform: translateX(0) } to{ transform: translateX(700px) }}效果:其中 drive 是该keyframes的名称,from, to 是keyframes播放过程的时间起点和终点,时间点也可以用百分比表示,如50%,from, to 等价于 0%, 100%。每个时间点对应一个css状态,代表一个关键帧(keyframe)。keyframes定义完成后使用方法如下:animation也有简写形式,如:animation: slidein 3s ease-in 1s infinite reverse both running;但这种对书写顺序有一定要求(delay要写在duration后面,其他参数无顺序要求,css会通过传入的关键词识别)。(2) Animation Delay通过animation-delay,我们可以给动画延迟执行:animation-delay: 2s;(3) Animation Fill Modeforwards在上面的例子中可以看到马里奥运动到右边之后又回到了起点,如果我们想让他运动完成后就停留在右边呢?很简单,我们设置annimation fill mode就可以了:animation-fill-mode: forwardsforwards 表示动画完成后,元素将保持最后一帧的状态。backwards与之相对的还有 backwards,backwards表示并不是动画完成后元素变回第一帧的状态,而是表示当设置了animation-delay时,在动画开始前的等待过程中,立刻给元素应用第一帧的状态。我们将上面的例子稍作修改看一下效果:.animated{ animation-name: drive; animation-duration: 1s; animation-fill-mode: backwords; animation-delay: 1s; animation-timing-function: linear;}@keyframes drive { from{ transform: translateX(350px) } to{ transform: translateX(700px) scale(2) }}效果:可以看到,动画开始之前小人立马移动到350px处,1s之后才开始动画。both显而易见,both会同时应用forwards和backwards两种规则,即在delay时先应用第一帧的状态,结束时保持最后一帧的状态。(3) Animation Repeat我们可以通过 animation-iteration-count 设置动画循环播放的次数,比如:animation-iteration-count: 3;// 无限循环animation-iteration-count:infinite;就像这样:(4) Animation Directionnormal正常方向,也是默认方向,即先from,再to。reverse与正常方向相反,即先to,再from。例如:.animated{ … animation-direction: reverse;}@keyframes drive { from{ transform: translateX(-100px) rotateY(180deg) } to{ transform: translateX(862px) rotateY(180deg)}}效果:alternatealternate意为“交替”,即normal和reverse交替之行,先normal再reverse。reverse alternate反向交替,先reverse再normal。(4) Animation Timing function和transition一样,keyframes也可以设置timing function,常用的timing function归纳如下:ease:默认方法,初速度较慢,然后加速再减速ease-in:初速度最慢,然后一直加速ease-out:初速度最快,然后一直减速ease-in-out:初速度较慢,然后加速再减速,与ease的区别在于加速减速过程是对称的linear:恒速运动直观表现如下(codepen):除了上面现成的方法,我们可以通过贝塞尔曲线自定义速度曲线。我们可以在 http://cubic-bezier.com 可视化的创建我们自己的贝塞尔曲线。比如创建一个刚开始极慢,突然变得极快的曲线:css:animation-timing-function: cubic-bezier(1,.03,1,-0.03);效果:是不是挺神奇!(5) Chain Animation我们可以将多个animation串联使用,比如我们想让小人在行驶的过程中跳跃,可以这么写:css:.mario { … animation: drive 3s both infinite linear, jump 0.5s 1.2s ease-in-out infinite;}@keyframes jump { 0% { top: -40px; } 50% { top: -120px; } 100% { top: -40px; }}效果:实践本文目的在于普及css3 animation的基础,并未完全覆盖animation的知识,未涉及和讲解的知识请大家见谅 。 掌握上述知识后,我们就已经可以用animation做出丰富的动画效果了,下面列出一些codepen上的小例子:full mario demoanimated popupfly items to shopping cartflipping cards ...

December 25, 2018 · 2 min · jiezi

10种方法实现一个tips带有描边的小箭头

转载:10种方法实现一个tips带有描边的小箭头我们在网页开发中实现一个tips时会有一个小箭头,实现这种方法的文章网上已经泛滥了,但有时实现这个小箭头不止只有单纯的三角它还有描边,今天我们就借那些现有的文章在深入一点来说说如何给tips小箭头描边,本章不涉及svg/canvas,没必要因为我讲的是css。主体样式:<div class=“dui-tips”><a href=“http://www.w3cbest.com”>w3cbest我是一个tips</a></div> .dui-tips{ position: relative; padding: 10px; text-align: center; border: 1px solid #f60; border-radius: 5px; background-color: #fff; }第一种border描边双层覆盖:就是大家常用的border,实现原理就是给其中一条边设置颜色宽度及样式,我这里使用了两个伪类进行折叠,将一个白色的覆盖在有颜色的伪类上面,再偏移1px来模拟实现1px的描边效果,代码如下:.dui-tips { &:before, &:after { position: absolute; left: 50%; display: table; width: 0; height: 0; content: ‘’; transform: translate(-50%, 0); border-width: 10px 10px 0 10px; border-style: solid; } &:before { z-index: 0; bottom: -10px; border-color: #f60 transparent transparent transparent; } &:after { z-index: 1; bottom: -8px; border-color: #fff transparent transparent transparent; }}第二种border描边结合滤镜drop-shadow属性:第二种是第一种的延伸,使用滤镜filter的drop-shadow描边来实现,box-shadow和drop-shadow实现不规则投影.dui-tips { &:after { position: absolute; left: 50%; display: table; width: 0; height: 0; content: ‘’; transform: translate(-50%, 0); border-width: 10px 10px 0 10px; border-style: solid; } &:after { bottom: -9px; border-color: #fff transparent transparent transparent; filter: drop-shadow(0px 2px 0px #f60); }}第三种通过特殊符号"◆“字体双层覆盖第三种方法和第一种类似,通过两层颜色叠加在有层级的偏移来实现.dui-tips { &:before, &:after { font-size: 24px; line-height: 18px; position: absolute; left: 50%; display: table; content: ‘◆’; transform: translate(-50%, 0); text-align: center; } &:before { z-index: 0; bottom: -10px; color: #f60; } &:after { z-index: 1; bottom: -8px; color: #fff; }}第四种通过text-shadow实现这种放发通过给文子设置1px的阴影来显描边效果.dui-tips { &:after { font-size: 24px; line-height: 18px; position: absolute; left: 50%; display: table; content: ‘◆’; transform: translate(-50%, 0); text-align: center; } &:after { z-index: 1; bottom: -8px; color: #fff; text-shadow: 0 2px 0 #f60; }}第五种 background双层覆盖这种方式设置两个宽度和高度分别为10px的方块背景,再给两层背景分别设置不同的颜色,再通过两层背景颜色叠加,经过层级偏移再有transform的rotate属性旋转角度,来实现箭头的朝向。.dui-tips { &:before, &:after { position: absolute; left: 50%; display: table; width: 10px; height: 10px; content: ‘’; margin-left: -5px; transform: rotate(-45deg); } &:before { z-index: 0; bottom: -6px; background-color: #f60; } &:after { z-index: 1; bottom: -5px; background-color: #fff; }}第六种background和border背景描边旋转此方法就是设置一个宽度和高度分别为10px的方块背景,然后背景相邻的两条边描边再有transform的rotate属性旋转角度,来实现箭头的朝向。.dui-tips { &:after { position: absolute; left: 50%; display: table; width: 10px; height: 10px; margin-left: -5px; content: ‘’; transform: rotate(-45deg); } &:after { z-index: 1; bottom: -6px; border-bottom: 1px solid #f60; border-left: 1px solid #f60; background-color: #fff; }}第七种background和box-shadow.dui-tips { &:after { position: absolute; left: 50%; display: table; width: 10px; height: 10px; content: ‘’; margin-left: -5px; transform: rotate(-45deg); } &:after { z-index: 1; bottom: -5px; background-color: #fff; box-shadow: -1px 1px 0 #f60; }}第八种linear-gradient.dui-tips{ &:before, &:after{ position: absolute; left: 50%; display: table; width: 10px; height: 10px; content: ‘’; margin-left: -5px; transform: rotate(-135deg); } &:before { z-index: 0; bottom: -6px; background: linear-gradient(-45deg, transparent 7px, #f60 0); } &:after { z-index: 1; bottom: -5px; background: linear-gradient(-45deg, transparent 7px, #fff 0); }}第九种linear-gradient和box-shadow.dui-tips{ &:after{ position: absolute; left: 50%; display: table; width: 10px; height: 10px; content: ‘’; margin-left: -5px; transform: rotate(-135deg); } &:after { z-index: 1; bottom: -5px; background: linear-gradient(-45deg, transparent 7px, #fff 0); box-shadow: -1px -1px 0 #f60 }}第十种linear-gradient和border.dui-tips{ &:after{ position: absolute; left: 50%; display: table; width: 10px; height: 10px; content: ‘’; margin-left: -5px; transform: rotate(-135deg); } &:after { z-index: 1; bottom: -6px; background: linear-gradient(-45deg, transparent 7px, #fff 0); border-top: 1px solid #f60; border-left: 1px solid #f60; }}转载:10种方法实现一个tips带有描边的小箭头 ...

December 25, 2018 · 2 min · jiezi

前端 CSS : 3# 纯 CSS 实现粉色爱心

介绍原文链接感謝 comehope 大佬的 [前端每日实战]工作三个月觉得糟糕跑路(顺带劝一下像我一样的新人, 不要急于入职, 一定要挑一挑)回家重新补充了一下基础知识及 node, 身为一个前端结果发现 CSS 已经手生了, 为了明年能够找到工作, 所以又开始练习了…效果预览github.io 浏览源代码地址https://github.com/shanyuhai1…代码解读1. 首先是完成 html 结构我们需要五颗爱心及底部的 footer<figure class=“hearts”> <section class=“heart”></section> <section class=“heart”></section> <section class=“heart”></section> <section class=“heart”></section> <section class=“heart”></section></figure><footer>pink hearts</footer>样式初始化及居中body { margin: 0; height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: #f3f3f3; overflow: hidden;}.hearts { width: 100vw; height: 20vw; border: 1px solid; /* the snippet will be deleted / box-sizing: border-box; padding: 0 5vw; display: flex; align-items: center; justify-content: space-between;}.heart { width: 15vw; height: 15vw; border: 1px solid; / the snippet will be deleted /}footer { margin-top: 10vh; text-transform: uppercase; letter-spacing: 2px; font-family: “verdana”; font-size: 22px; color: #F48FB1;}2. 画出旋转的 粉红正方形接着在第一个 heart 中添加一个粉红的正方形添加 DOM 结构<section class=“heart”> <div class=“plane”> <div class=“half-heart”></div> </div></section>基准面(plane) 定位并完成基础样式.heart { position: relative; display: flex; align-items: center; justify-content: center;}.plane { position: absolute; opacity:0.8;}.half-heart { width: 7.5vw; height: 7.5vw; background-color: pink; transform: rotate(-45deg);}一颗爱心由两个基准面组成<section class=“heart”> <div class=“plane plane-left”> <div class=“half-heart”></div> </div> <div class=“plane plane-right”> <div class=“half-heart”></div> </div></section>.heart { transform-style: preserve-3d;}.plane-right { transform: rotateY(90deg); / 因为此处为 90 度垂直, 所以并不可见 /}接着添加旋转动画(这样我们就可以看到两个基准面了).heart { animation: rotate 5s ease-in-out infinite;}.heart:nth-of-type(1) { animation-delay:-5s;}/ keyframes */@keyframes rotate { 0% { transform: rotateY(0deg) rotateZ(25deg) translateY(7.5vw); } 50% { transform: rotateY(270deg) rotateZ(25deg) translateY(-7.5vw); } 100% { transform: rotateY(360deg) rotateZ(25deg) translateY(7.5vw); }}3. 将旋转的两个正方形改为心形生成两个圆形放置在正方的上方即可(伪元素可解决).half-heart:before,.half-heart:after { content: “”; width: 7.5vw; height: 7.5vw; background-color: pink; border-radius: 100%; position: absolute;}.half-heart:before { top: -3.25vw; left: 0;}.half-heart:after { top: 0; left: 3.25vw;}好了, 这样一个旋转上升的爱心就完成了4. 补完 5 个 heart修改延迟时间(DOM 结构省略).heart:nth-of-type(1) { animation-delay:-5s;}.heart:nth-of-type(2) { animation-delay:-4s;}.heart:nth-of-type(3) { animation-delay:-3s;}.heart:nth-of-type(4) { animation-delay:-2s;}.heart:nth-of-type(5) { animation-delay:-1s;}5. 最后最后记得把之前确认位置及大小的 border 边框删除即可 ...

December 24, 2018 · 2 min · jiezi

mix-blend-mode及background-blend-mode实现炫酷的图片样式

在网上看到了有如相机功能的mix-blend-mode实现比较好看的图片样式,自己也想弄下,在这里记录下1.效果图2.mix-blend-mode相关属性{ mix-blend-mode: normal; // 正常 mix-blend-mode: multiply; // 正片叠底 mix-blend-mode: screen; // 滤色 mix-blend-mode: overlay; // 叠加 mix-blend-mode: darken; // 变暗 mix-blend-mode: lighten; // 变亮 mix-blend-mode: color-dodge; // 颜色减淡 mix-blend-mode: color-burn; // 颜色加深 mix-blend-mode: hard-light; // 强光 mix-blend-mode: soft-light; // 柔光 mix-blend-mode: difference; // 差值 mix-blend-mode: exclusion; // 排除 mix-blend-mode: hue; // 色相 mix-blend-mode: saturation; // 饱和度 mix-blend-mode: color; // 颜色 mix-blend-mode: luminosity; // 亮度 mix-blend-mode: initial; mix-blend-mode: inherit; mix-blend-mode: unset;}3.在css上加mix-blend-modehtml文件<div id=“root”> <select id=“select”> <option value =“normal”>normal – 正常</option> <option value =“multiply”>multiply – 正片叠底</option> <option value=“screen”>screen – 滤色</option> <option value=“overlay”>overlay – 叠加</option> <option value=“darken”>darken – 变暗</option> <option value=“lighten”>lighten – 变亮</option> <option value=“color-dodge”>color-dodge – 颜色减淡</option> <option value=“color-burn”>color-burn – 颜色加深</option> <option value=“hard-light”>hard-light – 强光</option> <option value=“soft-light”>soft-light – 柔光</option> <option value=“difference”>difference – 差值</option> <option value=“exclusion”>exclusion – 排除</option> <option value=“hue”>hue – 色相</option> <option value=“saturation”>saturation – 饱和度</option> <option value=“color”>color – 颜色</option> <option value=“luminosity”>luminosity – 亮度</option> </select> <div class=“container”> <div class=“item mode1”></div> <div class=“item mode2”></div> <div class=“item mode3”></div> </div></div><script> (function() { let selectElement = document.getElementById(‘select’); let item = document.getElementsByClassName(‘item’); selectElement.addEventListener(‘change’, function() { for(let i = 0; i < item.length; i++) { item[i].style.cssText = ‘mix-blend-mode:’ + selectElement.value; } }); })()</script>css样式.container{ position: relative; margin: 140px auto; width: 120px; height: 120px;}.item{ position: absolute; top: 0; left: 0; width: 100px; height: 100px; border-radius: 50%; mix-blend-mode: normal;}.mode1{ transform: translateX(30%); background: rgba(255, 0, 255, .8);}.mode2{ transform: translateX(-30%); background: rgba(0, 255, 255, .8);}.mode3{ transform: translateY(-50%); background: rgba(0, 255, 0, .8);}#select{ position: absolute; left: 500px; top: 100px;}4.使用background-blend-modecss文件.root { width: 400px; height: 500px; margin: 20px auto; background: url(../images/22.jpg), url(https://user-images.githubusercontent.com/8554143/34369175-c14ae23e-eaf4-11e7-96f1-e146e5e5a96b.jpg); background-size: cover; /background-blend-mode: lighten;/}#root{ position: absolute; top: 50px; left: 50px;}ul{ margin: 0; padding: 0; list-style: none;}/* 下拉框包含层 /#selectedItem{ width: 240px; cursor: pointer;}/ 已选中的选项 /#promptText{ position: relative; padding-left: 10px; width: 230px; height: 30px; line-height: 30px; border: 1px solid #d3d3d3; border-radius: 4px; background: #fff; color: #999; font-size: 14px;}/ 图标 /#arrows{ position: absolute; top: 0; right: 0; width: 30px; height: 30px; vertical-align: middle;}#arrows:focus{ outline: none;}/ 下拉可选项包含层 /.choiceDescription{ position: absolute; display: none; /overflow: hidden;/ margin-top: 2px; width: 240px; border: 1px solid #ccc; border-radius: 4px; box-shadow: 0 1px 6px rgba(0, 0, 0, .1); background: #fff;}.show{ display: block;}/ 下拉可选项 /.item{ height: 30px; line-height: 30px; padding-left: 10px; font-size: 15px; color: #666;}.item:hover, .active{ color: #fff; background: rgba(49, 255, 195, 0.67);}html文件<div class=“root”></div><div id=“root”> <div id=“selectedItem”> <div id=“promptText”><span id=“spanText”>请选择你喜欢的文字</span><img src="../images/arrowup.png" id=“arrows” /></div> <ul class=“choiceDescription”> <li class=“item”>normal–正常</li> <li class=“item”>multiply–正片叠底</li> <li class=“item”>screen–滤色</li> <li class=“item”>overlay–叠加</li> <li class=“item”>darken–变暗</li> <li class=“item”>lighten–变亮</li> <li class=“item”>color-dodge–颜色减淡</li> <li class=“item”>color-burn–颜色加深</li> <li class=“item”>hard-light–强光</li> <li class=“item”>soft-light–柔光</li> <li class=“item”>difference–差值</li> <li class=“item”>exclusion–排除</li> <li class=“item”>hue–色相</li> <li class=“item”>saturation–饱和度</li> <li class=“item”>color–颜色</li> <li class=“item”>luminosity–亮度</li> </ul> </div></div><script> (function() { let rootElement = document.getElementsByClassName(‘root’); let choiceDescription = document.getElementsByClassName(‘choiceDescription’)[0]; let arrows = document.getElementById(‘arrows’); / 用于判断是否是下拉 / let isDown = false; let selectedItem = document.getElementById(‘selectedItem’); / 对点击下拉进行监听 / selectedItem.addEventListener(‘click’, function() { isDown = !isDown; if(isDown) { / 如果是下拉状态,则显示下拉的选项,并把图标显示为向下的图标 / choiceDescription.className += ’ show’; arrows.src = ‘../images/arrowdown.png’; } else { choiceDescription.className = ‘choiceDescription’; arrows.src = ‘../images/arrowup.png’; } / 对已经选中的选项进行标记 / let itemElement = document.getElementsByClassName(‘item’); let promptText = document.getElementById(‘spanText’); for(let i = 0; i < itemElement.length; i++) { itemElement[i].className = ‘item’; if(promptText.innerHTML == itemElement[i].innerHTML) { itemElement[i].className = ‘item active’; } } }); choiceDescription.addEventListener(‘click’, function(e) { let promptText = document.getElementById(‘spanText’); let selectElement = e.target; / 判断是否点击的是li标签,防止点击了li标签以外的空白位置 / while(selectElement.tagName !== ‘LI’) { / 如果点中的是当前容器层 / if(selectElement == choiceDescription) { selectElement = null; break; } / 若果不是,则再找父级容器 / selectElement = selectElement.parentNode; } / innerText、innerHTML、value * innerText 是指html标签里的文字信息,单纯的文本,不会有html标签,存在兼容性 * innerHTML 是指包含在html标签里的所有子元素,包括空格、html标签 * value 表单里的元素属性值 * */ if(selectElement) { promptText.innerHTML = e.target.innerHTML; rootElement[0].style.cssText = ‘background-blend-mode:’ + e.target.innerHTML.split(’–’)[0]; } }); })()</script>5.注意项mix-blend-mode及background-blend-mode存在兼容性正在努力学习中,若对你的学习有帮助,留下你的印记呗(点个赞咯^_^)往期好文推荐:判断iOS和Android及PC端纯css实现瀑布流(multi-column多列及flex布局)实现单行及多行文字超出后加省略号微信小程序之购物车和父子组件传值及calc的注意事项 ...

December 21, 2018 · 3 min · jiezi

用css3实现ps蒙版效果+动画

说实话,css3越来越强大,使用css也可以做越来越多意想不到的效果。今天,见到有人用css3实现了ps的蒙版效果,如下所示:实现原理这个动画,其实也并不复杂。它使用background-clip实现了文字蒙版的效果,然后结合了背景图片的animation实现了如上图所示的文字蒙版动画。用css3做蒙版效果常见的有两种,一种是图形的,另一种是文字的。图形蒙版这里要使用的到时clip-path,它的作用是根据你指定的图形的轮廓来保留剩余的区域,如果你制定的图形是圆形,那么剩余的就是个圆形。它有个特点就是,你可以把你的盒子模型定义为不规则的图形。我们都知道原来的css只允许我们把元素定义成矩形和正方形,又可以在矩形区域内嵌套其他的矩形或者正方形元素。而现在我们可以定义一个不规则的图形,然后在这个不规则的图形内定义其他的元素。这样说可能还是有点儿含糊。假如你在一个元素内填充满文字,以前只能沿着矩形或者正方形的边缘填充,而现在使用clip-path可以使文字沿着不规则的图形的边缘填充。具体可参考文字环绕接着说图形蒙版,有两种实现:一种是保留剪切图形轮廓内的内容,可以参考shpape-masking。另一种使保留剪切图内之外的内容,可参考Reverse clip path。这个动画效果是由背景的gif和视频结合的,当然也可以使用css3的animation。文字蒙版文字蒙版使用的使css3中的backgorund-clip,这个属性支持border-box,padding-box,content-box和text等属性,具体使用可参考mdn background-clip。它和clip的效果类似,都是剪切后剩余部分的内容,text 这个属性值比较特殊,针对的是元素内的文字,其他的针对的是元素内内容的显示区域。背景的动画很简单就是一个animation动画。参考示例:Merry Christmas

December 19, 2018 · 1 min · jiezi

10分钟理解CSS3 Grid

基本介绍上一篇文章我们介绍了 css3 flexbox,今天我们再来说说css3的另外一个强大的功能:Grid。Grid做前端的同学应该都很熟悉了,翻译成中文为“栅格”,用过bootstrap、semantic ui、ant design的同学肯定都了解grid layout(删格布局),以往css框架中的grid布局一般是通过float和百分比的宽度实现的,这种实现有几种缺点:html不够简洁;需要清除浮动以避免高度塌陷;列的个数是固定的,不能灵活定义。比如bootstrap是12列,semantic ui是16列,ant design 24列。当然grid也可以用flex实现,但是并不会比用float简单多少,而且flex擅长的是一维空间的布局,而对grid这种二维空间并不擅长。现在css3从规范和标准层面实现了grid,编程体验大大提升!兼容性用法Grid作为一个二维的栅格系统,由若干列(column)和行(row)构成。1. Column(1) 设置columnCSS3中的Grid可以划分为任意个数的列(column),而且每个column的宽度可以任意设置!我们先来看一个简单的例子:html:<div id=“content”> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> <div>6</div> <div>7</div> <div>8</div> <div>9</div></div>css:body{ color: #fff; text-align: center;}#content{ display: grid; grid-template-columns: 33.3% 33.3% 33.3%; max-width: 960px; margin: 0 auto;}#content div{ background: lightgrey; padding: 30px;}#content div:nth-child(even){ background: skyblue;}效果:当我们设置了display: grid和grid-template-columns: 33.3% 33.3% 33.3%后#content就被划分成了三行三列的grid,此时#content被称为grid container,而#content的子元素称为grid item。我们也可以任意改变column的个数和宽度,比如:#content{ grid-template-columns: 10% 20% 30% 40%;}效果:(2) fr(fraction)css3中引入了一个新的单位fr(fraction),中文意思为“分数”,用于替代百分比,因为百分比(小数)存在除不尽的情况,用分数表示可以避免多位小数的写法。比如三列等宽的grid可以表示为:grid-template-columns: 1fr 1fr 1fr;(3) repeat我们也可以用repeat方法来简化column或者row的写法,repeat方法接受两个参数,第一个参数表示重复的次数,第二个参数表示重复的内容。所以,三列等宽的grid我们还可以表示为:grid-template-columns: repeat(3, 1fr);当我们要定义的列数很多时,repeat就会变得非常有用,比如我们要定义一个10列等宽的grid,可以写成repeat(10, 1fr),而不用将1fr重复书写10遍。2. Row(1) 设置row当我们设置column之后,row会因为元素的换行而自动产生,但是我们依然可以设置row的个数和高度。css:#content{ display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(4, 60px); max-width: 960px;}效果:可以看到,虽然第四行没有内容,但是row确实存在并占据了那部分空间。(2) minmax上面的例子中我们给了row一个固定高度,这导致一个问题:如果某个grid item中的内容特别多,受制于固定的高度,部分内容将无法显示,如下图:为解决这个问题,css提供了minmax函数,让我们可以设置row的最小高度和最大高度,最大高度取auto后便可以让row的高度自适应:css:grid-auto-rows: minmax(60px, auto);// 或者grid-template-rows: repeat(3, minmax(60px, auto));效果:(3) grid gap如果我们想给行和列之间加上间隔,也有现成的方法:css:grid-gap:{ 10px;}效果:3. Grid Line以上所有例子中,grid中的每个grid item都是按默认顺序排列的。如果我们想重新布局改变grid item的位置或大小呢?为此引入了grid lines的概念,所谓的grid lines就是将grid若干等分后的分割线,如下图中横向和纵向序号1~8的线即为grid lines:<img src=“http://lc-jOYHMCEn.cn-n1.lcfi…; alt=“drawing” width=“500”/>通过定义grid item的起始和结束的grid line我们就可以实现对grid item位置和覆盖面积的控制。一个简单的例子:html:<div id=“content”> <div class=“one”>1</div></div>css: #content { display: grid; grid-template-columns: repeat(8, 100px); grid-template-rows: repeat(8, 100px); grid-gap: 10px;}.one { grid-column-start: 3; grid-column-end: 6; grid-row-start: 3; grid-row-end: 6;}效果:通过设置grid-column-start/end grid-row-start/end 相当于给grid item设置起始坐标和结束坐标,上面的css也可以简写为:.one { grid-column: 3 / 6; grid-row: 3 / 6;}// 或者.one { grid-area: 3 / 3 / 6 / 6;}如果grid item的起始grid line为默认,我们可以只设置它的跨度(span):.one{ grid-column: span 3; grid-row: span 3;}4. Grid Area Template除了通过grid lines进行布局,css3提供了一种更牛逼的布局方式:grid area template。与其用语言解释什么是grid area template,不如直接看代码:html:<div id=“content”> <header>Header</header> <main>Main</main> <section>Section</section> <aside>Aside</aside> <nav>Nav</nav> <footer>Footer</footer></div>css:body { color: #fff; text-align: center;}#content { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: minmax(100px, auto); max-width: 960px; margin: 0 auto; grid-gap: 10px; grid-template-areas: “header header header header” “aside . main main” “nav . main main” “section section section section” “section section section section” “footer footer footer footer”;}#content>* { background: #3bbced; padding: 30px;}header { grid-area: header; }main { grid-area: main; }section{ grid-area: section; }aside { grid-area: aside; }nav { grid-area: nav; }footer { grid-area: footer; }效果:看明白没有?重点在于grid container的 grid-template-areas 属性。我们给每个grid item设置一个grid area,然后在grid container中设置一个grid area模版(grid-template-areas),模版中每行字符串表示一个row,每个area名称表示一个column,完全将几何布局用文字模拟出来,空白的grid item用 . 表示。当然使用grid area要注意语法严谨,像 “header main header main” 这种写法css是无法解析的,用area名称模拟出的结构在二维空间上必须是一个整体,因为每个grid item也是无法分割的。 使用grid area template的有点在实现响应式布局时也是显而易见的,我们只需要针对不同的屏幕尺寸制定不同的grid area template就行了。5. Justify and Align与flex类似,grid也可以设置justify和align来调整grid item横向和纵向对齐方式。同样也同时支持对grid container或单个grid item进行设置。对grid container设置html:<div id=“content”> <div class=“one”>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> <div>6</div></div>css:#content { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, minmax(120px, auto)); grid-gap: 10px; max-width: 960px; align-items: start; justify-items: end;}效果:注意:flex里面用的是 justify-content 而grid里面是 justify-items ,flex里面的值是 flex-start/flex-end ,而grid里面是 start/end 。justify和align的默认值都是 stretch 。对grid item设置css:.one{ align-self: start; justify-self: end;}效果:实践掌握了上述知识,我们就能用CSS3 Grid快速做出各种layout效果了,附上几个简单的codepen示例:12列grid布局花瓣式布局响应式布局 ...

December 19, 2018 · 2 min · jiezi

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

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

December 13, 2018 · 1 min · jiezi

10分钟理解CSS3 FlexBox

基本介绍特点flexbox是一种css display类型,提供一种更简单高效的布局方式;flexbox可以对元素相对于父元素、兄弟元素进行定位、控制尺寸、控制间距;flexbox对响应式有很好的支持;工作原理设置父元素的display属性为flex,则子元素都变成flex item,由此可以控制子元素的排列方式、尺寸、间距等;兼容性Flex Container先来看一个最简单的flex示例,外层div设置display: flex成为一个flex container,内部的3个div则自动变为flex item:html:<div class=“flex-container”> <div class=“box one”></div> <div class=“box two”></div> <div class=“box three”></div></div>css:.flex-container{ max-width: 960px; margin: 0 auto; display:flex; }.box{ height: 100px; min-width: 100px; }.one{ background: pink; }.two{ background: lightgreen; }.three{ background: skyblue; }效果:效果与浮动布局类似,但是如果用浮动实现的话需要写更多的代码,而flex一行就搞定了。1. Justify Content如果我们想让flex item居中排列呢,我们可以给flex container增加一个css属性:justify-content,它控制flex item在主轴方向(main axis,由flex-drection决定,默认为水平方向)上的对齐方式:.flex-container{ … justify-content: center;}效果如图:除此之外justify-content还可以设置为flex-start, flex-end, space-around, space-between, space-even等值,具体效果请自行实验。2. Align Items实现了flex方向的居中后,垂直于主轴方向(cross axis)的居中可以用align-items实现。css:.flex-container{ max-width: 960px; margin: 0 auto; display:flex; justify-content: center; height: 200px; background-color: white; align-items: center;}效果:使用flex解决了以往css垂直居中实现复杂的问题!相应的,align-items还有flex-start, flex-end等其他值。3. Flex Directionflex-direction决定了主轴方向即flex item排列方向,除了默认的row方向之外,还可以纵向、反向(row-reverse/column-reverse)排列flex item:css:.flex-container{ … flex-direction: column; align-items: center;}效果:4. Flex Wrap如果我们不想在窗口变窄的情况下压缩flex item,而是让超出边界的flex item换行显示那我们可以设置flex container的flex-wrap:.flex-container{ max-width: 960px; margin: 0 auto; display:flex; flex-wrap: wrap;}.box{ height: 100px; min-width: 300px; flex-grow: 1;}当我们压缩窗口的时候,效果如下:flex wrap还有一个值:wrap-reverse,设置该值后,被wrap的元素会排到其他元素上面:由此可见,flex wrap一定程度上可以取代media query了。5. Flex Row最后,flex-direction和flex-wrap可以合并为一个属性flex-flow,比如:flex-flow: row-reverse wrap。Flex Item1. Flex Grow在上面所有的例子中,三个flex item只占据了flex container小部分空间,如果想让flex item占满flex container我们需要给flex item设置flex-grow属性。顾名思义,grow意味着增长,用于控制flex item的尺寸的伸展。将css修改为:.box { height: 100px; min-width: 100px; flex-grow:1; }效果:可以看到三个子元素平分了父元素的空间,因为此时它们的flex-grow都是1。如果只有一个子元素设置了flex-grow呢?css:.box{ height: 100px; min-width: 100px; }.one{ background: pink; flex-grow: 1; }效果:此时two和three的大小不变,而one占据了父元素剩余空间。如果将one的flex-grow改为2,而two和three改为1,我们看看会发生什么:css:.box{ height: 100px; min-width: 100px; flex-grow:1; }.one{ background: pink; flex-grow: 2; }效果:可以看到one的宽度变成了two和three的两倍,因此flex item的尺寸和flex-grow的值成正比。2. Flex Shrink与flex-grow相对的是flex-shrink, flex-shrink用于控制子元素尺寸超过flex container后,对子元素的压缩。请看示例:修改box的宽度为flex container的1/3,one、two、three的flex-shrink分别为1,2,3:.box{ height: 100px; width: 320px; }.one{ background: pink; flex-shrink: 1; }.two{ background: lightgreen; flex-shrink: 2; }.three{ background: skyblue; flex-shrink: 3; }当窗口正常尺寸时,效果如下:当我们压缩窗口使其变得更窄后,效果如下:当flex container宽度变为540px后,子元素都被不同程度的压缩了。压缩后的one、two、three的宽度分别为250px、180px、110px,所以相比于初始宽度320px被压缩掉的宽度分别为70px、140px、210px,70 : 140 : 210 = 1 : 2 : 3,与flex shrink的值成反比。实际上压缩率和flex item的初始尺寸也有关系,只不过当初始尺寸一样时它带来的影响被忽略了。假设flex shrink为fs,flex item的初始尺寸为is,flex item被压缩的尺寸为ss,则正确的表达式为:fs ∝ is/ss3. Flex Basisflex-basis用于设置flex item的初始宽/高。为什么有width和height还需要重新加一个flex-basis呢?flex-basis和width/height有如下的区别:flex-basis只能用于flex-item,而width/height可以应用于其他类型的元素;flex-basis和flex-direction有关,当flex-direction为row的时,flex-basis设置的是宽度,当flex-direction为column时,flex-basis设置的是高度;当flex item被绝对定位后(absolute position),flex-basis不起作用,而width/height可以;flex-basis可以用于flex的简写形式,如:flex: 1 0 200px;我们来看一下flex-basis的作用,将css修改如下:.box{ height: 100px; flex-grow: 1;}.one{ background: pink; flex-basis: 100px;}.two{ background: lightgreen; flex-basis: 200px;}.three{ background: skyblue; flex-basis: 300px;}3个flex item都在原来的初始宽度基础上增加了相同的宽度:当然,这个例子如果换成使用width也是一样的效果,但是虽然效果一样但意义不一样,所以使用flex布局时还是应该尽量遵守规范,选合适的人去干正确的事。4. Order通过order属性我们可以改变flex item的排列顺序,例如:html:<section id=“blocks”> <div class=“one”>1</div> <div class=“two”>2</div> <div class=“three”>3</div> <div class=“four”>4</div></section>css:#blocks{ display: flex; margin: 10px; justify-content: space-between;}#blocks div{ flex: 0 0 100px; padding: 40px 0; text-align: center; background: #ccc;}默认排列顺序是按照flex item在html中的出现顺序:当我们修改flex item的order值后,flex item会按照order值升序排列:css:.one{ order: 4; }.two{ order: 3; }.three{ order: 2; }.four{ order: 1; }效果:结语flex就先简单介绍到这里,flex很强大也很简单,希望大家用的开心。 ...

December 13, 2018 · 2 min · jiezi

原生js实现瀑布流及微信小程序中使用左右两列实现瀑布流

使用css实现瀑布流并不实用,因为潮汕市实现的瀑布流都是以列来排列的,这里记录下用js实现瀑布流,以及微信小程序中使用左右两列来实现瀑布流1.效果图2.原生js实现瀑布流html文件<div id=“root”> <div class=“item”> <div class=“itemImg”> <img src="../images/1.jpeg" alt="" /> </div> </div> <div class=“item”> <div class=“itemImg”> <img src="../images/3.jpeg" alt="" /> </div> </div> <div class=“item”> <div class=“itemImg”> <img src="../images/2.jpg" alt="" /> </div> </div></div>图片可以自己找点替换下就可以了css文件*{ margin: 0; padding: 0;}#root{ position: relative;}.item{ float: left; padding: 5px;}/* 添加阴影的时候,加上border会显得更加有点悬浮感 /.itemImg{ padding: 5px; border: 1px solid #ccc; box-shadow: 0 0 5px #ccc; border-radius: 5px;}.itemImg img{ width: 230px; height: auto;}js文件window.onload = function () { / 计算图片列数及获取最小高度图片 / generateImg(‘root’, ‘item’); / 对窗口大小改变进行监听,大小改变则重新布局 / window.addEventListener(‘resize’, function() { generateImg(‘root’, ‘item’) }); / 图片对象 / let imgData = { images: [ { “src”:“23.png” }, { “src”:“22.png” }, { “src”:“2.jpg” }, { “src”:“4.jpg” }, { “src”:“7.jpg” } ] }; / 对滚动监听 / window.addEventListener(‘scroll’, function() { if(checkIsScroll()) { let rootElement = document.getElementById(‘root’); / 利用documentFragment来创建 /// let documentFragment = document.createDocumentFragment(); let length = imgData.images.length; / 循环创建图片组 / for(let i = 0; i < length; i++) { let itemElement = document.createElement(‘div’); itemElement.className = ‘item’; rootElement.appendChild(itemElement); let itemImgElement = document.createElement(‘div’); itemImgElement.className = ‘itemImg’; itemElement.appendChild(itemImgElement); let itemImg = document.createElement(‘img’); itemImg.style.cssText = ‘opacity: 0; transform:scale(0)’; itemImg.src = “../images/” + imgData.images[i].src; itemImgElement.appendChild(itemImg);// documentFragment.appendChild(itemElement); / 在1秒后让图片显示出来 / (function(img){ setTimeout(function(){ img.style.cssText=“opacity:1;transform:scale(1)”; },1000); })(itemImg); }// rootElement.appendChild(documentFragment); generateImg(‘root’, ‘item’); } });};/ 计算图片列数及获取最小高度图片 /function generateImg(parent, content) { / 获取父元素及其所以节点内容 / let parentElement = document.getElementById(parent); let childContent = getChildElement(parentElement, content); / 获取图片宽度 / let imgWidth = childContent[0].offsetWidth; / 获取一行图片形成的列数 / let imgColumn = Math.floor(document.documentElement.clientWidth / imgWidth); / 重新设置父级容器的宽度 / parentElement.style.cssText = ‘width:’ + imgColumn * imgWidth + ‘px;margin:0 auto’; / 存储每个图片的高度,以此来找到最小图片高 / let imgHeightArray = []; let length = childContent.length; for(let i = 0; i < length; i++) { / i<imgColumn统计每一行的图片高度 / if(i < imgColumn) { / 防止用户改变窗口大小时,内容样式错乱 / childContent[i].style.cssText = ‘’; imgHeightArray.push(childContent[i].offsetHeight); } else { / 如果不是这一行的,则找到最小值和最小值的索引值 / let minHeight = getMinImgHeight(imgHeightArray); let minHeightIndex = getMinHeightIndex(imgHeightArray, minHeight); / 对这个图片设置位置 / childContent[i].style.position = ‘absolute’; childContent[i].style.top = minHeight + ‘px’; childContent[i].style.left = childContent[minHeightIndex].offsetLeft + ‘px’; / 更换此时的最小高度 / imgHeightArray[minHeightIndex] = childContent[i].offsetHeight + minHeight; } }}/ 检测滚动是否达到了可视区 /function checkIsScroll() { / 获取root根节点 / let parentElement = document.getElementById(‘root’); / 获取父元素下的类名为box的元素节点 / let childContent = getChildElement(parentElement, ‘item’); / 获取最后一个元素的高度 / let lastElementHeight = childContent[childContent.length - 1].offsetTop; / 获取滚动的距离 / let scrollTopSpace = document.documentElement.scrollTop || document.body.scrollTop; / 获取可视区的距离 / let clientHeight = document.documentElement.clientHeight || document.body.clientHeight; if(lastElementHeight > scrollTopSpace + clientHeight) { return true; }}/ 获取子节点的所有内容 /function getChildElement(parentElement, content) { / 存储元素信息 / let elementArray = []; / 获取父元素下的所有节点信息 / let allElement = parentElement.getElementsByTagName(’’); let length = allElement.length; for (let i = 0; i < length; i++) { /* 找到对应的类名 / if (allElement[i].className === content) { elementArray.push(allElement[i]); } } return elementArray;}/ 获取图片最小高度 /function getMinImgHeight(heightArray) { let length = heightArray.length; let minHeight = heightArray[0]; for(let i = 0; i < length; i++) { minHeight = Math.min(minHeight, heightArray[i]); } return minHeight;}/ 获取图片最小高度的索引值 /function getMinHeightIndex(heightArray, minHeight) { let length = heightArray.length; for(let i = 0; i < length; i++) { if(heightArray[i] == minHeight) { return i; } }}3.微信小程序中实现瀑布流效果图wxml文件<view class=“cateCommodity”> <view class=“leftContainer”> <block wx:for="{{imageArray}}" wx:key="{{item.id}}"> <view class=“cateItem” wx:if="{{index%2==0}}"> <view class=“item”> <image src="{{item.src}}" class=“itemImg” mode=“widthFix”></image> <view class=“title”>{{item.title}}</view> </view> </view> </block> </view> <view class=“rightContainer”> <block wx:for="{{imageArray}}" wx:key="{{item.id}}"> <view class=“cateItem” wx:if="{{index%2==1}}"> <view class=“item”> <image src="{{item.src}}" class=“itemImg” mode=“widthFix”></image> <view class=“title”>{{item.title}}</view> </view> </view> </block> </view></view><view class=“skipTop” catchtap=“skipTop” wx:if="{{showTopImage}}"> <image src=“http://boweisou.oss-cn-shenzhen.aliyuncs.com/images/0/2018/11/ZBtqujbbcGjBDgjt0bbJqbTuGqq0z8.png"></image></view>wxss文件page{ background: #f6f6f6;}/ 最外层 /.cateCommodity { display: flex; padding: 20rpx 28rpx 8rpx; box-sizing: border-box; font-size: 28rpx;}/ 左右两个容器 /.leftContainer{ display: flex; margin-right: 22rpx; flex-direction: column;}.rightContainer{ display: flex; flex-direction: column;}/ 图片容器 /.cateItem { margin-bottom: 20rpx;}.item{ padding: 20rpx 22rpx; width: 335rpx; box-sizing: border-box; background: #fff; border-radius: 6rpx;}.itemImg{ margin-bottom: 14rpx; width: 100%; vertical-align: middle; border-radius: 6rpx;}.title{ display: -webkit-box; overflow: hidden; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.5;}/ 返回顶部 /.skipTop { position: fixed; bottom: 30rpx; right: 20rpx; width: 90rpx; height: 90rpx;}.skipTop image { width: 100%; height: 100%; vertical-align: middle;}js文件Page({ data: { imageArray: [ { id: 1, src: ‘../../images/avatar.jpeg’, title: ‘现代新中式创意陶瓷简约摆件客厅家居玄关软装饰品家居酒柜盘子’ }, { id: 1, src: ‘../../images/avatar3.jpg’, title: ‘秋冬季新款2018休闲运动服套装女士韩版金丝绒卫衣加绒加厚两件套’ }, { id: 1, src: ‘../../images/avatar4.jpeg’, title: ‘女童床上用品四件套公主房1.2m床品纯棉女孩1.8儿童床单三件套1.5’ }, { id: 1, src: ‘../../images/avatar7.jpg’, title: ‘婴儿床圆床蚊帐落地款宝宝椭圆床蚊帐支架款儿童床蚊帐BB床小蚊帐’ }, { id: 1, src: ‘../../images/avatar9.jpeg’, title: ‘包邮动感158T速滑鞋轮滑鞋竞速鞋高端碳纤鞋 固定码 专业定制’ }, { id: 1, src: ‘../../images/logo7.jpg’, title: ‘Infanton落地婴儿床蚊帐带支架儿童床蚊帐宝宝蚊帐婴童蚊帐’ }, { id: 1, src: ‘../../images/logo6.jpg’, title: ‘老A轮滑 米高seba hl碳纤版SEBA HL CARBON 平花鞋刹车鞋全能鞋’ }, { id: 1, src: ‘../../images/logo.jpeg’, title: ‘洋洋法代 sandro 17秋冬 一粒扣羊毛长款大衣外套EKIN M9575H’ }, ], showTopImage: false, }, onPageScroll(event) { / 利用两个条件,防止重复的进行setData操作 / if (event.scrollTop > 300 && this.data.showTopImage == false) { this.setData({ showTopImage: true }) } else if (event.scrollTop < 300 && this.data.showTopImage == true) { this.setData({ showTopImage: false }) } }, skipTop() { / 返回顶部 */ wx.pageScrollTo({ scrollTop: 0, duration: 300 }); this.setData({ showTopImage: false }); }, onReachBottom: function () { let temporaryArray = this.data.imageArray; temporaryArray.push(…this.data.imageArray); this.setData({ imageArray: temporaryArray }) },})左右两列实现瀑布流其实就是对同一数组进行了两次渲染,只是把其中的一半给隐藏了正在努力学习中,若对你的学习有帮助,留下你的印记呗(点个赞咯^_^)往期好文推荐:判断iOS和Android及PC端纯css实现瀑布流(multi-column多列及flex布局)实现单行及多行文字超出后加省略号微信小程序之购物车和父子组件传值及calc的注意事项 ...

December 11, 2018 · 4 min · jiezi

值得看看,2019 年 11 个受欢迎的 JavaScript 动画库!

1.Three.js超过46K的星星,这个流行的库提供了非常多的3D显示功能,以一种直观的方式使用 WebGL。这个库提供了<canvas>、 <svg>、CSS3D 和 WebGL渲染器,让我们在设备和浏览器之间创建丰富的交互体验。该库于2010年4月首次推出,目前仍有近1000名贡献者在开发中。2. Anime.js超过20K的星星,Anime是一个JavaScript动画库,可以处理CSS属性,单个CSS转换,SVG或任何DOM属性以及JavaScript对象。 此库允许您链接多个动画属性,将多个实例同步,创建时间轴等。3.Mo.js超过 14K 星星,是一个用于 Web 的动态图形工具带,具有简单的声明 API,跨设备兼容性和超过1500个单元测试。 您可以在DOME或SVG DOME周围移动东西或创建唯一的 mo.js 对象。虽然文档有些稀缺,但是示例很丰富,这里有CSS技巧的介绍。4. Velocity超过 15k 星星,Velocity是一个快速的 Javascript 动画引擎,拥有与jQuery的 $.animate() 相同的API。它具有彩色动画、转换、循环、画架、SVG支持和滚动。这里是Velocity的高性能引擎的分解,这里是使用该库的 SVG 动画的介绍。5. Popmotion超过 14K 星星,这个动画库大小只有 11 kb。它允许开发人员从动作创建动画和交互,这些动作是可以启动和停止,可以使用CSS、SVG、React、three创建,js和任何接受数字作为输入的API。6. Vivus超过 10k 星星,Vivus是一个零依赖的JavaScript类,可以让你为SVG制作动画,让它们具有被绘制的外观。 您可以使用许多可用动画之一,或创建自定义脚本来绘制SVG。 查看Vivus-instant获取实际示例,亲自动动手练习一下。7. GreenSock JSGSAP 是一个JavaScript库,用于创建高性能、零依赖、跨浏览器动画,据称在超过400万个网站上使用。GSAP是灵活的,可以与React、Vue、Angular和vanilla JS协同工作。GSDevtools 还可以帮助使用GSAP构建dubug动画。8. Scroll Reveal拥有15K颗星星和零依赖,这个库为 web 和移动浏览器提供了简单的滚动动画,以动画的方式显示滚动中的内容。它支持多种简洁的效果类型,甚至允许你使用自然语言定义动画。这里有一个简短的 SitePoint教程。9. Hover (CSS)超过 20k 星星,Hover提供了CSS3支持的悬停效果集合,可应用于链接、按钮、徽标、SVG、特色图像等,在CSS、Sass和LESS中可用。您=可以复制和粘贴希望在自己的样式表中使用的效果,或者引用样式。10. Kute.js一个完全成熟的原生JavaScript动画引擎,具有跨浏览器动画的基本功能。 重点是代码质量,灵活性,性能和大小(核心引擎17k 和 gzipped 5.5k) - 这是一个演示。 该库也是可扩展的,因此你可以添加自己的功能。11. Typed.js超过 7k 星星,这个库基允许你以选定的速度为字符串创建打字动画。 你还可以在页面上放置 HTML div 并从中读取以允许搜索引擎和禁用JavaScript的用户访问,由Slack和其他人使用,这个库既流行又非常有用。你的点赞是我持续分享好东西的动力,欢迎点赞!一个笨笨的码农,我的世界只能终身学习!更多内容请关注公众号《大迁世界》!

December 10, 2018 · 1 min · jiezi

前端每日实战:164# 视频演示如何用原生 JS 和 GSAP 创作一个数独训练小游戏(内含 4 个视频)

效果预览按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。https://codepen.io/comehope/pen/mQYobz可交互视频此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。请用 chrome, safari, edge 打开观看。第 1 部分:https://scrimba.com/p/pEgDAM/c7Q86ug第 2 部分:https://scrimba.com/p/pEgDAM/ckgBNAD第 3 部分:https://scrimba.com/p/pEgDAM/cG7bWc8第 4 部分:https://scrimba.com/p/pEgDAM/cez34fp源代码下载每日前端实战系列的全部源代码请从 github 下载:https://github.com/comehope/front-end-daily-challenges代码解读解数独的一项基本功是能迅速判断一行、一列或一个九宫格中缺少哪几个数字,本项目就是一个训练判断九宫格中缺少哪个数字的小游戏。游戏的流程是:先选择游戏难度,有 Easy、Normal、Hard 三档,分别对应着九宫格中缺少 1 个、2 个、3 个数字。开始游戏后,用键盘输入九宫格中缺少的数字,如果全答出来了,就会进入下一局,一共 5 局,5 局结束之后这一次游戏就结束了。在游戏过程中,九宫格的左上角会计时,右上角会计分。整个游戏分成 4 个步骤开发:静态页面布局、程序逻辑、计分计时和动画效果。一、页面布局定义 dom 结构,.app 是整个应用的容器,h1 是游戏标题,.game 是游戏的主界面。.game 中的子元素包括 .message 和 .digits,.message 用来提示游戏时间 .time、游戏的局数 .round、得分 .score,.digits 里是 9 个数字:<div class=“app”> <h1>Sudoku Training</h1> <div class=“game”> <div class=“message”> <p> Time: <span class=“time”>00:00</span> </p> <p class=“round”>1/5</p> <p> Score: <span class=“score”>100</span> </p> </div> <div class=“digits”> <span>1</span> <span>2</span> <span>3</span> <span>4</span> <span>5</span> <span>6</span> <span>7</span> <span>8</span> <span>9</span> </div> </div></div>居中显示:body { margin: 0; height: 100vh; display: flex; align-items: center; justify-content: center; background: silver; overflow: hidden;}定义应用的宽度,子元素纵向布局:.app { width: 300px; display: flex; flex-direction: column; align-items: center; justify-content: space-between; user-select: none;}标题为棕色字:h1 { margin: 0; color: sienna;}提示信息是横向布局,重点内容加粗:.game .message { width: inherit; display: flex; justify-content: space-between; font-size: 1.2em; font-family: sans-serif;}.game .message span { font-weight: bold;}九宫格用 grid 布局,外框棕色,格子用杏白色背景:.game .digits { box-sizing: border-box; width: 300px; height: 300px; padding: 10px; border: 10px solid sienna; display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 10px;}.game .digits span { width: 80px; height: 80px; background-color: blanchedalmond; font-size: 30px; font-family: sans-serif; text-align: center; line-height: 2.5em; color: sienna; position: relative;}至此,游戏区域布局完成,接下来布局选择游戏难度的界面。在 html 文件中增加 .select-level dom 结构,它包含一个难度列表 levels 和一个开始游戏的按钮 .play,游戏难度分为 .easy、.normal 和 .hard 三个级别:<div class=“app”> <h1>Sudoku Training</h1> <div class=“game”> <!– 略 –> </div> <div class=“select-level”> <div class=“levels”> <input type=“radio” name=“level” id=“easy” value=“easy” checked=“checked”> <label for=“easy”>Easy</label> <input type=“radio” name=“level” id=“normal” value=“normal”> <label for=“normal”>Normal</label> <input type=“radio” name=“level” id=“hard” value=“hard”> <label for=“hard”>Hard</label> </div> <div class=“play”>Play</div> </div></div>为选择游戏难度容器画一个圆形的外框,子元素纵向布局:.select-level { z-index: 2; box-sizing: border-box; width: 240px; height: 240px; border: 10px solid rgba(160, 82, 45, 0.8); border-radius: 50%; box-shadow: 0 0 0 0.3em rgba(255, 235, 205, 0.8), 0 0 1em 0.5em rgba(160, 82, 45, 0.8); display: flex; flex-direction: column; align-items: center; font-family: sans-serif;}布局 3 个难度选项,横向排列:.select-level .levels { margin-top: 60px; width: 190px; display: flex; justify-content: space-between;}把 input 控件隐藏起来,只显示它们对应的 label:.select-level .levels { position: relative;}.select-level input[type=radio] { visibility: hidden; position: absolute; left: 0;}设置 label 的样式,为圆形按钮:.select-level label { width: 56px; height: 56px; background-color: rgba(160, 82, 45, 0.8); border-radius: 50%; text-align: center; line-height: 56px; color: blanchedalmond; cursor: pointer;}当某个 label 对应的 input 被选中时,令 label 背景色加深,以示区别:.select-level input[type=radio]:checked + label { background-color: sienna;}设置开始游戏按钮 .play 的样式,以及交互效果:.select-level .play { width: 120px; height: 30px; background-color: sienna; color: blanchedalmond; text-align: center; line-height: 30px; border-radius: 30px; text-transform: uppercase; cursor: pointer; margin-top: 30px; font-size: 20px; letter-spacing: 2px;}.select-level .play:hover { background-color: saddlebrown;}.select-level .play:active { transform: translate(2px, 2px);}至此,选择游戏难度的界面布局完成,接下来布局游戏结束界面。游戏结束区 .game-over 包含一个 h2 标题,二行显示最终结果的段落 p 和一个再玩一次的按钮 .again。最终结果包括最终耗时 .final-time 和最终得分 .final-score:<div class=“app”> <h1>Sudoku Training</h1> <div class=“game”> <!– 略 –> </div> <div class=“select-level”> <!– 略 –> </div> <div class=“game-over”> <h2>Game Over</h2> <p> Time: <span class=“final-time”>00:00</span> </p> <p> Score: <span class=“final-score”>3000</span> </p> <div class=“again”>Play Again</div> </div> </div>因为游戏结束界面和选择游戏难度界面的布局相似,所以借用 .select-level 的代码:.select-level,.game-over { z-index: 2; box-sizing: border-box; width: 240px; height: 240px; border: 10px solid rgba(160, 82, 45, 0.8); border-radius: 50%; box-shadow: 0 0 0 0.3em rgba(255, 235, 205, 0.8), 0 0 1em 0.5em rgba(160, 82, 45, 0.8); display: flex; flex-direction: column; align-items: center; font-family: sans-serif;}标题和最终结果都用棕色字:.game-over h2 { margin-top: 40px; color: sienna;}.game-over p { margin: 3px; font-size: 20px; color: sienna;}“再玩一次”按钮 .again 的样式与开始游戏 .play 的样式相似,所以也借用 .play 的代码:.select-level .play,.game-over .again { width: 120px; height: 30px; background-color: sienna; color: blanchedalmond; text-align: center; line-height: 30px; border-radius: 30px; text-transform: uppercase; cursor: pointer;}.select-level .play { margin-top: 30px; font-size: 20px; letter-spacing: 2px;}.select-level .play:hover,.game-over .again:hover { background-color: saddlebrown;}.select-level .play:active,.game-over .again:active { transform: translate(2px, 2px);}.game-over .again { margin-top: 10px;}把选择游戏难度界面 .select-level 和游戏结束界面 .game-over 定位到游戏容器的中间位置:.app { position: relative;}.select-level,.game-over { position: absolute; bottom: 40px;}至此,游戏界面 .game、选择游戏难度界面 .select-level 和游戏结束界面 .game-over 均已布局完成。接下来为动态程序做些准备工作。把选择游戏难度界面 .select-level 和游戏结束界面 .game-over 隐藏起来,当需要它们呈现时,会在脚本中设置它们的 visibility 属性:.select-level,.game-over { visibility: hidden;}游戏中,当选择游戏难度界面 .select-level 和游戏结束界面 .game-over 出现时,应该令游戏界面 .game 变模糊,并且加一个缓动时间,.game.stop 会在脚本中调用:.game { transition: 0.3s;}.game.stop { filter: blur(10px);}游戏中,当填错了数字时,要把错误的数字描一个红边;当填对了数字时,把数字的背景色改为巧克力色。.game .digits span.wrong 和 .game .digits span.correct 会在脚本中调用:.game .digits span.wrong { border: 2px solid crimson;}.game .digits span.correct { background-color: chocolate; color: gold;}至此,完成全部布局和样式设计。二、程序逻辑引入 lodash 工具库,后面会用到 lodash 提供的一些数组函数:<script src=“https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>在写程序逻辑之前,先定义几个存储业务数据的常量。ALL_DIGITS 存储了全部备选的数字,也就是从 1 到 9;ANSWER_COUNT 存储的是不同难度要回答的数字个数,easy 难度要回答 1 个数字,normal 难度要回答 2 个数字,hard 难度要回答 3 个数字;ROUND_COUNT 存储的是每次游戏的局数,默认是 5 局;SCORE_RULE 存储的是答对和答错时分数的变化,答对加 100 分,答错扣 10 分。定义这些常量的好处是避免在程序中出现魔法数字,提高程序可读性:const ALL_DIGITS = [‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’]const ANSWER_COUNT = {EASY: 1, NORMAL: 2, HARD: 3}const ROUND_COUNT = 5const SCORE_RULE = {CORRECT: 100, WRONG: -10}再定义一个 dom 对象,用于引用 dom 元素,它的每个属性是一个 dom 元素,key 值与 class 类名保持一致。其中大部分 dom 元素是一个 element 对象,只有 dom.digits 和 dom.levels 是包含多个 element 对象的数组;另外 dom.level 用于获取被选中的难度,因为它的值随用户选择而变化,所以用函数来返回实时结果:const $ = (selector) => document.querySelectorAll(selector)const dom = { game: $(’.game’)[0], digits: Array.from($(’.game .digits span’)), time: $(’.game .time’)[0], round: $(’.game .round’)[0], score: $(’.game .score’)[0], selectLevel: $(’.select-level’)[0], level: () => {return $(‘input[type=radio]:checked’)[0]}, play: $(’.select-level .play’)[0], gameOver: $(’.game-over’)[0], again: $(’.game-over .again’)[0], finalTime: $(’.game-over .final-time’)[0], finalScore: $(’.game-over .final-score’)[0],}在游戏过程中需要根据游戏进展随时修改 dom 元素的内容,这些修改过程我们也把它们先定义在 render 对象中,这样程序主逻辑就不用关心具体的 dom 操作了。render 对象的每个属性是一个 dom 操作,结构如下:const render = { initDigits: () => {}, updateDigitStatus: () => {}, updateTime: () => {}, updateScore: () => {}, updateRound: () => {}, updateFinal: () => {},}下面我们把这些 dom 操作逐个写下来。render.initDigits 用来初始化九宫格。它接收一个文本数组,根据不同的难度级别,数组的长度可能是 8 个(easy 难度)、7 个(normal 难度)或 6 个(hard 难度),先把它补全为长度为 9 个数组,数量不足的元素补空字符,然后把它们随机分配到九宫格中:const render = { initDigits: (texts) => { allTexts = texts.concat(_.fill(Array(ALL_DIGITS.length - texts.length), ‘’)) _.shuffle(dom.digits).forEach((digit, i) => { digit.innerText = allTexts[i] digit.className = ’’ }) }, //…}render.updateDigitStatus 用来更新九宫格中单个格子的状态。它接收 2 个参数,text 是格子里的数字,isAnswer 指明这个数字是不是答案。格子的默认样式是浅色背景深色文字,如果传入的数字不是答案,也就是答错了,会为格子加上 wrong 样式,格子被描红边;如果传入的数字是答案,也就是答对了,会在一个空格子里展示这个数字,并为格子加上 correct 样式,格子的样式会改为深色背景浅色文字:const render = { //… updateDigitStatus: (text, isAnswer) => { if (isAnswer) { let digit = _.find(dom.digits, x => (x.innerText == ‘’)) digit.innerText = text digit.className = ‘correct’ } else { _.find(dom.digits, x => (x.innerText == text)).className = ‘wrong’ } }, //…}render.updateTime 用来更新时间,render.updateScore 用来更新得分:const render = { //… updateTime: (value) => { dom.time.innerText = value.toString() }, updateScore: (value) => { dom.score.innerText = value.toString() }, //…}render.updateRound 用来更新当前局数,显示为 “n/m” 的格式:const render = { //… updateRound: (currentRound) => { dom.round.innerText = [ currentRound.toString(), ‘/’, ROUND_COUNT.toString(), ].join(’’) }, //…}render.updateFinal 用来更新游戏结束界面里的最终成绩:const render = { //… updateFinal: () => { dom.finalTime.innerText = dom.time.innerText dom.finalScore.innerText = dom.score.innerText },}接下来定义程序整体的逻辑结构。当页面加载完成之后执行 init() 函数,init() 函数会对整个游戏做些初始化的工作 ———— 令开始游戏按钮 dom.play 被点击时调用 startGame() 函数,令再玩一次按钮 dom.again 被点击时调用 playAgain() 函数,令按下键盘时触发事件处理程序 pressKey() ———— 最后调用 newGame() 函数开始新游戏:window.onload = initfunction init() { dom.play.addEventListener(‘click’, startGame) dom.again.addEventListener(‘click’, playAgain) window.addEventListener(‘keyup’, pressKey) newGame()}function newGame() { //…}function startGame() { //…}function playAgain() { //…}function pressKey() { //…}当游戏开始时,令游戏界面变模糊,呼出选择游戏难度的界面:function newGame() { dom.game.classList.add(‘stop’) dom.selectLevel.style.visibility = ‘visible’}当选择了游戏难度,点击开始游戏按钮 dom.play 时,隐藏掉选择游戏难度的界面,游戏界面恢复正常,然后把根据用户选择的游戏难度计算出的答案数字个数存储到全局变量 answerCount 中,调用 newRound() 开始一局游戏:let answerCountfunction startGame() { dom.game.classList.remove(‘stop’) dom.selectLevel.style.visibility = ‘hidden’ answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound()}当一局游戏开始时,打乱所有候选数字,生成一个全局数组变量 digits,digits 的每个元素包含 3 个属性,text 属性表示数字文本,isAnswer 属性表示该数字是否为答案,isPressed 表示该数字是否被按下过,isPressed 的初始值均为 false,紧接着把 digits 渲染到九宫格中:let digitsfunction newRound() { digits = .shuffle(ALL_DIGITS).map((x, i) => { return { text: x, isAnwser: (i < answerCount), isPressed: false } }) render.initDigits(.filter(digits, x => !x.isAnwser).map(x => x.text))}当用户按下键盘时,若按的键不是候选文本,就忽略这次按键事件。通过按键的文本在 digits 数组中找到对应的元素 digit,判断该键是否被按过,若被按过,也退出事件处理。接下来,就是针对没按过的键,在对应的 digit 对象上标明该键已按过,并且更新这个键的显示状态,如果用户按下的不是答案数字,就把该数字所在的格子描红,如果用户按下的是答案数字,就突出显示这个数字:function pressKey(e) { if (!ALL_DIGITS.includes(e.key)) return; let digit = .find(digits, x => (x.text == e.key)) if (digit.isPressed) return; digit.isPressed = true render.updateDigitStatus(digit.text, digit.isAnwser)}当用户已经按下了所有的答案数字,这一局就结束了,开始新一局:function pressKey(e) { if (!ALL_DIGITS.includes(e.key)) return; let digit = .find(digits, x => (x.text == e.key)) if (digit.isPressed) return; digit.isPressed = true render.updateDigitStatus(digit.text, digit.isAnwser) //判断用户是否已经按下所有的答案数字 let hasPressedAllAnswerDigits = (.filter(digits, (x) => (x.isAnwser && x.isPressed)).length == answerCount) if (!hasPressedAllAnswerDigits) return; newRound()}增加一个记录当前局数的全局变量 round,在游戏开始时它的初始值为 0,每局游戏开始时,它的值就加1,并更新游戏界面中的局数 dom.round:let roundfunction newGame() { round = 0 //初始化局数 dom.game.classList.add(‘stop’) dom.selectLevel.style.visibility = ‘visible’}function startGame() { render.updateRound(1) //初始化页面中的局数 dom.game.classList.remove(‘stop’) dom.selectLevel.style.visibility = ‘hidden’ answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound()}function newRound() { digits = .shuffle(ALL_DIGITS).map((x, i) => { return { text: x, isAnwser: (i < answerCount), isPressed: false } }) render.initDigits(.filter(digits, x => !x.isAnwser).map(x => x.text)) //每局开始时为局数加 1 round++ render.updateRound(round)}当前局数 round 增加到常量 ROUND_COUNT 定义的游戏总局数,本次游戏结束,调用 gameOver() 函数,否则调用 newRound() 函数开始新一局:function pressKey(e) { if (!ALL_DIGITS.includes(e.key)) return; let digit = .find(digits, x => (x.text == e.key)) if (digit.isPressed) return; digit.isPressed = true render.updateDigitStatus(digit.text, digit.isAnwser) let hasPressedAllAnswerDigits = (.filter(digits, (x) => (x.isAnwser && x.isPressed)).length == answerCount) if (!hasPressedAllAnswerDigits) return; //判断是否玩够了总局数 let hasPlayedAllRounds = (round == ROUND_COUNT) if (hasPlayedAllRounds) { gameOver() } else { newRound() }}游戏结束时,令游戏界面变模糊,调出游戏结束界面,显示最终成绩:function gameOver() { render.updateFinal() dom.game.classList.add(‘stop’) dom.gameOver.style.visibility = ‘visible’}在游戏结束界面,用户可以点击再玩一次按钮 dom.again,若点击了此按钮,就把游戏结束界面隐藏起来,开始一局新游戏,这就回到 newGame() 的流程了:function playAgain() { dom.game.classList.remove(‘stop’) dom.gameOver.style.visibility = ‘hidden’ newGame()}至此,整个游戏的流程已经跑通了,此时的脚本如下:const ALL_DIGITS = [‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’]const ANSWER_COUNT = {EASY: 1, NORMAL: 2, HARD: 3}const ROUND_COUNT = 3const SCORE_RULE = {CORRECT: 100, WRONG: -10}const $ = (selector) => document.querySelectorAll(selector)const dom = { game: $(’.game’)[0], digits: Array.from($(’.game .digits span’)), time: $(’.game .time’)[0], round: $(’.game .round’)[0], score: $(’.game .score’)[0], selectLevel: $(’.select-level’)[0], level: () => {return $(‘input[type=radio]:checked’)[0]}, play: $(’.select-level .play’)[0], gameOver: $(’.game-over’)[0], again: $(’.game-over .again’)[0], finalTime: $(’.game-over .final-time’)[0], finalScore: $(’.game-over .final-score’)[0],}const render = { initDigits: (texts) => { allTexts = texts.concat(.fill(Array(ALL_DIGITS.length - texts.length), ‘’)) _.shuffle(dom.digits).forEach((digit, i) => { digit.innerText = allTexts[i] digit.className = ’’ }) }, updateDigitStatus: (text, isAnswer) => { if (isAnswer) { let digit = _.find(dom.digits, x => (x.innerText == ‘’)) digit.innerText = text digit.className = ‘correct’ } else { _.find(dom.digits, x => (x.innerText == text)).className = ‘wrong’ } }, updateTime: (value) => { dom.time.innerText = value.toString() }, updateScore: (value) => { dom.score.innerText = value.toString() }, updateRound: (currentRound) => { dom.round.innerText = [ currentRound.toString(), ‘/’, ROUND_COUNT.toString(), ].join(’’) }, updateFinal: () => { dom.finalTime.innerText = dom.time.innerText dom.finalScore.innerText = dom.score.innerText },}let answerCount, digits, roundwindow.onload = initfunction init() { dom.play.addEventListener(‘click’, startGame) dom.again.addEventListener(‘click’, playAgain) window.addEventListener(‘keyup’, pressKey) newGame()}function newGame() { round = 0 dom.game.classList.add(‘stop’) dom.selectLevel.style.visibility = ‘visible’}function startGame() { render.updateRound(1) dom.game.classList.remove(‘stop’) dom.selectLevel.style.visibility = ‘hidden’ answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound()}function newRound() { digits = .shuffle(ALL_DIGITS).map((x, i) => { return { text: x, isAnwser: (i < answerCount), isPressed: false } }) render.initDigits(.filter(digits, x => !x.isAnwser).map(x => x.text)) round++ render.updateRound(round)}function gameOver() { render.updateFinal() dom.game.classList.add(‘stop’) dom.gameOver.style.visibility = ‘visible’}function playAgain() { dom.game.classList.remove(‘stop’) dom.gameOver.style.visibility = ‘hidden’ newGame()}function pressKey(e) { if (!ALL_DIGITS.includes(e.key)) return; let digit = .find(digits, x => (x.text == e.key)) if (digit.isPressed) return; digit.isPressed = true render.updateDigitStatus(digit.text, digit.isAnwser) let hasPressedAllAnswerDigits = (.filter(digits, (x) => (x.isAnwser && x.isPressed)).length == answerCount) if (!hasPressedAllAnswerDigits) return; let hasPlayedAllRounds = (round == ROUND_COUNT) if (hasPlayedAllRounds) { gameOver() } else { newRound() }}三、计分和计时接下来处理得分和时间,先处理得分。首先声明一个用于存储得分的全局变量 score,在新游戏开始之前设置它的初始值为 0,在游戏开始时初始化页面中的得分:let scorefunction newGame() { round = 0 score = 0 //初始化得分 dom.game.classList.add(‘stop’) dom.selectLevel.style.visibility = ‘visible’}function startGame() { render.updateRound(1) render.updateScore(0) //初始化页面中的得分 dom.game.classList.remove(‘stop’) dom.selectLevel.style.visibility = ‘hidden’ answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound()}在用户按键事件中根据按下的键是否为答案记录不同的分值:function pressKey(e) { if (!ALL_DIGITS.includes(e.key)) return; let digit = .find(digits, x => (x.text == e.key)) if (digit.isPressed) return; digit.isPressed = true render.updateDigitStatus(digit.text, digit.isAnwser) //累积得分 score += digit.isAnwser ? SCORE_RULE.CORRECT : SCORE_RULE.WRONG render.updateScore(score) let hasPressedAllAnswerDigits = (.filter(digits, (x) => (x.isAnwser && x.isPressed)).length == answerCount) if (!hasPressedAllAnswerDigits) return; let hasPlayedAllRounds = (round == ROUND_COUNT) if (hasPlayedAllRounds) { gameOver() } else { newRound() }}接下来处理时间。先创建一个计时器类 Timer,它的参数是一个用于把时间渲染到页面上的函数,另外 Timer 有 start() 和 stop() 2 个方法用于开启和停止计时器,计时器每秒会执行一次 tickTock() 函数:function Timer(render) { this.render = render this.t = {}, this.start = () => { this.t = setInterval(this.tickTock, 1000); } this.stop = () => { clearInterval(this.t) }}定义一个记录时间的变量 time,它的初始值为 0 分 0 秒,在 tickTock() 函数中把秒数加1,并调用渲染函数把当前时间写到页面中:function Timer(render) { this.render = render this.t = {} this.time = { minute: 0, second: 0, } this.tickTock = () => { this.time.second ++; if (this.time.second == 60) { this.time.minute ++ this.time.second = 0 } render([ this.time.minute.toString().padStart(2, ‘0’), ‘:’, this.time.second.toString().padStart(2, ‘0’), ].join(’’)) } this.start = () => { this.t = setInterval(this.tickTock, 1000) } this.stop = () => { clearInterval(this.t) }}在开始游戏时初始化页面中的时间:function startGame() { render.updateRound(1) render.updateScore(0) render.updateTime(‘00:00’) //初始化页面中的时间 dom.game.classList.remove(‘stop’) dom.selectLevel.style.visibility = ‘hidden’ answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound()}定义一个存储定时器的全局变量 timer,在创建游戏时初始化定时器,在游戏开始时启动计时器,在游戏结束时停止计时器:let timerfunction newGame() { round = 0 score = 0 timer = new Timer(render.updateTime) //创建定时器 dom.game.classList.add(‘stop’) dom.selectLevel.style.visibility = ‘visible’}function startGame() { render.updateRound(1) render.updateScore(0) render.updateTime(‘00:00’) dom.game.classList.remove(‘stop’) dom.selectLevel.style.visibility = ‘hidden’ answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound() timer.start() //开始计时}function gameOver() { timer.stop() //停止计时 render.updateFinal() dom.game.classList.add(‘stop’) dom.gameOver.style.visibility = ‘visible’}至此,时钟已经可以运行了,在游戏开始时从 0 分 0 秒开始计时,在游戏结束时停止计时。最后一个环节,当游戏结束之后,不应再响应用户的按键事件。为此,我们定义一个标明是否可按键的变量 canPress,在创建新游戏时它的状态是不可按,游戏开始之后变为可按,游戏结束之后再变为不可按:let canPressfunction newGame() { round = 0 score = 0 time = { minute: 0, second: 0 } timer = new Timer() canPress = false //初始化是否可按键的标志 dom.game.classList.add(‘stop’) dom.selectLevel.style.visibility = ‘visible’}function startGame() { render.updateRound(1) render.updateScore(0) render.updateTime(0, 0) dom.game.classList.remove(‘stop’) dom.selectLevel.style.visibility = ‘hidden’ answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound() timer.start(tickTock) canPress = true //游戏开始后,可以按键}function gameOver() { canPress = false //游戏结束后,不可以再按键 timer.stop() render.updateFinal() dom.game.classList.add(‘stop’) dom.gameOver.style.visibility = ‘visible’}在按键事件处理程序中,首先判断是否允许按键,若不允许,就退出事件处理程序:function pressKey(e) { if (!canPress) return; //判断是否允许按键 if (!ALL_DIGITS.includes(e.key)) return; let digit = .find(digits, x => (x.text == e.key)) if (digit.isPressed) return; digit.isPressed = true render.updateDigitStatus(digit.text, digit.isAnwser) score += digit.isAnwser ? SCORE_RULE.CORRECT : SCORE_RULE.WRONG render.updateScore(score) let hasPressedAllAnswerDigits = (.filter(digits, (x) => (x.isAnwser && x.isPressed)).length == answerCount) if (hasPressedAllAnswerDigits) { newRound() }}至此,计分计时设计完毕,此时的脚本如下:const ALL_DIGITS = [‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’]const ANSWER_COUNT = {EASY: 1, NORMAL: 2, HARD: 3}const ROUND_COUNT = 3const SCORE_RULE = {CORRECT: 100, WRONG: -10}const $ = (selector) => document.querySelectorAll(selector)const dom = { //略,与此前代码相同}const render = { //略,与此前代码相同}let answerCount, digits, round, score, timer, canPresswindow.onload = initfunction init() { //略,与此前代码相同}function newGame() { round = 0 score = 0 timer = new Timer(render.updateTime) canPress = false dom.game.classList.add(‘stop’) dom.selectLevel.style.visibility = ‘visible’}function startGame() { render.updateRound(1) render.updateScore(0) render.updateTime(0, 0) dom.game.classList.remove(‘stop’) dom.selectLevel.style.visibility = ‘hidden’ answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound() timer.start() canPress = true}function newRound() { //略,与此前代码相同}function gameOver() { canPress = false timer.stop() render.updateFinal() dom.game.classList.add(‘stop’) dom.gameOver.style.visibility = ‘visible’}function playAgain() { //略,与此前代码相同}function pressKey(e) { if (!canPress) return; if (!ALL_DIGITS.includes(e.key)) return; let digit = .find(digits, x => (x.text == e.key)) if (digit.isPressed) return; digit.isPressed = true render.updateDigitStatus(digit.text, digit.isAnwser) score += digit.isAnwser ? SCORE_RULE.CORRECT : SCORE_RULE.WRONG render.updateScore(score) let hasPressedAllAnswerDigits = (.filter(digits, (x) => (x.isAnwser && x.isPressed)).length == answerCount) if (!hasPressedAllAnswerDigits) return; let hasPlayedAllRounds = (round == ROUND_COUNT) if (hasPlayedAllRounds) { gameOver() } else { newRound() }}四、动画效果引入 gsap 动画库:<script src=“https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script>游戏中一共有 6 个动画效果,分别是九宫格的出场与入场、选择游戏难度界面的显示与隐藏、游戏结束界面的显示与隐藏。为了集中管理动画效果,我们定义一个全局常量 animation,它的每个属性是一个函数,实现一个动画效果,结构如下,注意因为选择游戏难度界面和游戏结束界面的样式相似,所以它们共享了相同的动画效果,在调用函数时要传入一个参数 element 指定动画的 dom 对象:const animation = { digitsFrameOut: () => { //九宫格出场 }, digitsFrameIn: () => { //九宫格入场 }, showUI: (element) => { //显示选择游戏难度界面和游戏结束界面 }, frameOut: (element) => { //隐藏选择游戏难度界面和游戏结束界面 },}确定下这几个动画的时机:function newGame() { round = 0 score = 0 timer = new Timer(render.updateTime) canPress = false //选择游戏难度界面 - 显示 dom.game.classList.add(‘stop’) dom.selectLevel.style.visibility = ‘visible’}function startGame() { render.updateRound(1) render.updateScore(0) render.updateTime(‘00:00’) //选择游戏难度界面 - 隐藏 dom.game.classList.remove(‘stop’) dom.selectLevel.style.visibility = ‘hidden’ answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound() timer.start() canPress = true}function newRound() { //九宫格 - 出场 digits = .shuffle(ALL_DIGITS).map((x, i) => { return { text: x, isAnwser: (i < answerCount), isPressed: false } }) render.initDigits(.filter(digits, x => !x.isAnwser).map(x => x.text)) //九宫格 - 入场 round++ render.updateRound(round)}function gameOver() { canPress = false timer.stop() render.updateFinal() //游戏结束界面 - 显示 dom.game.classList.add(‘stop’) dom.gameOver.style.visibility = ‘visible’}function playAgain() { //游戏结束界面 - 隐藏 dom.game.classList.remove(‘stop’) dom.gameOver.style.visibility = ‘hidden’ newGame()}把目前动画时机所在位置的代码移到 animation 对象中,九宫格出场和入场的动画目前是空的:const animation = { digitsFrameOut: () => { //九宫格出场 }, digitsFrameIn: () => { //九宫格入场 }, showUI: (element) => { //显示选择游戏难度界面和游戏结束界面 dom.game.classList.add(‘stop’) element.style.visibility = ‘visible’ }, hideUI: (element) => { //隐藏选择游戏难度界面和游戏结束界面 dom.game.classList.remove(‘stop’) element.style.visibility = ‘hidden’ },}在动画时机的位置调用 animation 对应的动画函数,因为动画是有执行时长的,下一个动画要等到上一个动画结束之后再开始,所以我们采用了 async/await 的语法,让相邻的动画顺序执行:async function newGame() { round = 0 score = 0 timer = new Timer(render.updateTime) canPress = false // 选择游戏难度界面 - 显示 await animation.showUI(dom.selectLevel)}async function startGame() { render.updateRound(1) render.updateScore(0) render.updateTime(‘00:00’) // 选择游戏难度界面 - 隐藏 await animation.hideUI(dom.selectLevel) answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound() timer.start() canPress = true}async function newRound() { //九宫格 - 出场 await animation.digitsFrameOut() digits = .shuffle(ALL_DIGITS).map((x, i) => { return { text: x, isAnwser: (i < answerCount), isPressed: false } }) render.initDigits(.filter(digits, x => !x.isAnwser).map(x => x.text)) //九宫格 - 入场 await animation.digitsFrameIn() round++ render.updateRound(round)}async function gameOver() { canPress = false timer.stop() render.updateFinal() // 游戏结束界面 - 显示 await animation.showUI(dom.gameOver)}async function playAgain() { // 游戏结束界面 - 隐藏 await animation.hideUI(dom.gameOver) newGame()}接下来就开始设计动画效果。animation.digitsFrameOut 是九宫格的出场动画,各格子分别旋转着消失。注意,为了与 async/await 语法配合,我们让函数返回了一个 Promise 对象:const animation = { digitsFrameOut: () => { return new Promise(resolve => { new TimelineMax() .staggerTo(dom.digits, 0, {rotation: 0}) .staggerTo(dom.digits, 1, {rotation: 360, scale: 0, delay: 0.5}) .timeScale(2) .eventCallback(‘onComplete’, resolve) }) }, //…}animation.digitsFrameIn 是九宫格的入场动画,它的动画效果是各格子旋转着出现,而且各格子的出现时间稍有延迟:const animation = { //… digitsFrameIn: () => { return new Promise(resolve => { new TimelineMax() .staggerTo(dom.digits, 0, {rotation: 0}) .staggerTo(dom.digits, 1, {rotation: 360, scale: 1}, 0.1) .timeScale(2) .eventCallback(‘onComplete’, resolve) }) }, //…}animation.showUI 是显示择游戏难度界面和游戏结束界面的动画,它的效果是从高处落下,并在底部小幅反弹,模拟物体跌落的效果:const animation = { //… showUI: (element) => { dom.game.classList.add(‘stop’) return new Promise(resolve => { new TimelineMax() .to(element, 0, {visibility: ‘visible’, x: 0}) .from(element, 1, {y: ‘-300px’, ease: Elastic.easeOut.config(1, 0.3)}) .timeScale(1) .eventCallback(‘onComplete’, resolve) }) }, //…}animation.hideUI 是隐藏选择游戏难度界面和游戏结束界面的动画,它从正常位置向右移出画面:const animation = { //… hideUI: (element) => { dom.game.classList.remove(‘stop’) return new Promise(resolve => { new TimelineMax() .to(element, 1, {x: ‘300px’, ease: Power4.easeIn}) .to(element, 0, {visibility: ‘hidden’}) .timeScale(2) .eventCallback(‘onComplete’, resolve) }) },}至此,整个游戏的动画效果就完成了,全部代码如下:const ALL_DIGITS = [‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’]const ANSWER_COUNT = {EASY: 1, NORMAL: 2, HARD: 3}const ROUND_COUNT = 3const SCORE_RULE = {CORRECT: 100, WRONG: -10}const $ = (selector) => document.querySelectorAll(selector)const dom = { //略,与增加动画前相同}const render = { //略,与增加动画前相同}const animation = { digitsFrameOut: () => { return new Promise(resolve => { new TimelineMax() .staggerTo(dom.digits, 0, {rotation: 0}) .staggerTo(dom.digits, 1, {rotation: 360, scale: 0, delay: 0.5}) .timeScale(2) .eventCallback(‘onComplete’, resolve) }) }, digitsFrameIn: () => { return new Promise(resolve => { new TimelineMax() .staggerTo(dom.digits, 0, {rotation: 0}) .staggerTo(dom.digits, 1, {rotation: 360, scale: 1}, 0.1) .timeScale(2) .eventCallback(‘onComplete’, resolve) }) }, showUI: (element) => { dom.game.classList.add(‘stop’) return new Promise(resolve => { new TimelineMax() .to(element, 0, {visibility: ‘visible’, x: 0}) .from(element, 1, {y: ‘-300px’, ease: Elastic.easeOut.config(1, 0.3)}) .timeScale(1) .eventCallback(‘onComplete’, resolve) }) }, hideUI: (element) => { dom.game.classList.remove(‘stop’) return new Promise(resolve => { new TimelineMax() .to(element, 1, {x: ‘300px’, ease: Power4.easeIn}) .to(element, 0, {visibility: ‘hidden’}) .timeScale(2) .eventCallback(‘onComplete’, resolve) }) },}let answerCount, digits, round, score, timer, canPresswindow.onload = initfunction init() { //略,与增加动画前相同}async function newGame() { round = 0 score = 0 timer = new Timer(render.updateTime) canPress = false await animation.showUI(dom.selectLevel)}async function startGame() { render.updateRound(1) render.updateScore(0) render.updateTime(‘00:00’) await animation.hideUI(dom.selectLevel) answerCount = ANSWER_COUNT[dom.level().value.toUpperCase()] newRound() timer.start() canPress = true}async function newRound() { await animation.digitsFrameOut() digits = .shuffle(ALL_DIGITS).map((x, i) => { return { text: x, isAnwser: (i < answerCount), isPressed: false } }) render.initDigits(.filter(digits, x => !x.isAnwser).map(x => x.text)) await animation.digitsFrameIn() round++ render.updateRound(round)}async function gameOver() { canPress = false timer.stop() render.updateFinal() await animation.showUI(dom.gameOver)}async function playAgain() { await animation.hideUI(dom.gameOver) newGame()}function pressKey(e) { //略,与增加动画前相同}function tickTock() { //略,与增加动画前相同}大功告成!最后,附上交互流程图,方便大家理解: ...

December 9, 2018 · 13 min · jiezi

最大限度的减少浏览器的重新布局(Reflow&Repaint)

减少浏览器重新布局是优化web性能的一个重要手段。这是因为重新布局是浏览器在请求网络资源后所做的一个必要的工作,这也是浏览器渲染web页面的重要机制(详情可参考浏览器的运行原理)。在浏览器获得新的资源后,它会重新计算文档中个元素的位置和形状,以便刷新web页面(可以是部分内容,也可以是全部),这个过程就是重新布局,有的人把这个过程称为web页面的重绘。但是在重新布局的过程中,浏览器会阻止用户在浏览器中的其它操作,那么很显然,了解重新布局对于提升web应用的性能很重要,尤其是它可以显著的提升用户的体验效果。当然除了了解重新布局外,我们还需要了解各种文档属性对浏览器重新布局的影响因素,如:DOM深度、CSS规则,以及样式的改变等。有的时候,对HTML文档中的单个元素进行重新布局可能会影响到它的父级元素,或者它的兄弟元素,以及它的子元素的重新布局。触发浏览器重新布局的因素用户操作页面的初始化加载调整浏览器窗口的大小HTML文档修改使用js修改样式而引起的计算,如:margin: 0 auto;在DOM中添加或移除元素修改某个元素的类(class & id)那么,有没有一个规范可以缩短页面进行重新布局的呢?答案是肯定的。减少浏览器重新布局的规范减少不必要的 DOM 深度。在 DOM 树中的一个级别进行修改可能会致使该树的所有级别(上至根节点,下至所修改节点的子级)都随之变化。这会导致花费更多的时间来执行重排。尽可能减少 CSS 规则的数量,并移除未使用的 CSS 规则。如果你想进行复杂的渲染修改(如:动画),请在浏览器重新布局流程外执行此操作。你可以使用 position-absolute 或 position-fixed 来实现此目的。避免使用不必要且复杂的 CSS 选择器,尤其是后代选择器,因为此类选择器会消耗更多的 CPU 处理能力来执行选择器匹配。具体的开发中要注意的地方可参考下面两篇文章,它们会告诉你如何书写css会有效减少浏览器重新布局。参考资料前端性能优化:细说浏览器渲染的重排与重绘回流 & 重绘:CSS性能让你的JAVASCRIPT慢了吗?

December 2, 2018 · 1 min · jiezi

css3 绘制画圆、扇形

css已经越来越强大了 ,可以使用它来绘制各种简单的形状,用于代替图片显示,这次的分享主要用到画圆,扇形实现圆形<div class=“circle”></div><style>.circle { border-radius: 50%; width: 80px; height: 80px; background: #666;}</style>效果如下: border-radius圆角的四个值按顺序取值分别为:左上、右上、右下、左下。这里只设置一个值,代表四个角的取值都为为50% 原理:border-radius: 50% 弯曲元素的边框以创建圆。 由于圆在任何给定点具有相同的半径,故宽和高都需要保证一样的值,不同的值将创建椭圆。实现扇形利用border-radius,实现90度角的扇形:<div class=“sector”></div><style>.sector{ border-radius:80px 0 0; width: 80px; height: 80px; background: #666;}</style>效果如下:原理:左上角是圆角,其余三个角都是直角:左上角的值为宽和高一样的值,其他三个角的值不变(等于0)。2、绘制任意角度的扇形效果如下:/绘制一个60度扇形//绘制一个85度扇形//绘制一个向右扇形,90度扇形//绘制一个颜色扇形 //绘制一个不同颜色半圆夹角 /完整代码如下:<div class=“shanxing shanxing1”> <div class=“sx1”></div> <div class=“sx2”></div></div><!–绘制一个85度扇形/–p><div class=“shanxing shanxing2”> <div class=“sx1”></div> <div class=“sx2”></div></div><!–绘制一个向右扇形,90度扇形–><div class=“shanxing shanxing3”> <div class=“sx1”></div> <div class=“sx2”></div></div><!–*绘制一个颜色扇形 */–p><div class=“shanxing shanxing4”> <div class=“sx1”></div> <div class=“sx2”></div></div><!–/*绘制一个不同颜色半圆夹角 */–><div class=“shanxing shanxing5”> <div class=“sx1”></div> <div class=“sx2”></div></div><style>.shanxing{ position: relative; width: 200px; height: 200px; border-radius: 100px; background-color: yellow;}.sx1{ position: absolute; width: 200px; height: 200px; transform: rotate(0deg); clip: rect(0px,100px,200px,0px); /这个clip属性用来绘制半圆,在clip的rect范围内的内容显示出来,使用clip属性,元素必须是absolute的 / border-radius: 100px; background-color: #f00; /-webkit-animation: an1 2s infinite linear; /}.sx2{ position: absolute; width: 200px; height: 200px; transform: rotate(0deg); clip: rect(0px,100px,200px,0px); border-radius: 100px; background-color: #f00; /-webkit-animation: an2 2s infinite linear;/}/绘制一个60度扇形/.shanxing1 .sx1{transform: rotate(-30deg);}.shanxing1 .sx2{transform: rotate(-150deg);}/绘制一个85度扇形/.shanxing2 .sx1{transform: rotate(-45deg);}.shanxing2 .sx2{transform: rotate(-140deg);}/绘制一个向右扇形,90度扇形/.shanxing3 .sx1{transform: rotate(45deg);}.shanxing3 .sx2{transform: rotate(-45deg);}/*绘制一个颜色扇形 */.shanxing4 .sx1{transform: rotate(45deg);background-color: #fff;}.shanxing4 .sx2{transform: rotate(-45deg);background-color: #fff;}/*绘制一个不同颜色半圆夹角 */.shanxing5 .sx1{transform: rotate(45deg);background-color: #f00;}.shanxing5 .sx2{transform: rotate(-45deg);background-color: #0f0; </style>前端的专业程度很强,80%的问题自己很难解决,而且会很浪费时间,一个小问题可以困扰一天,这样自信心会受到严重的打击!可以加入我的前端学习q.u.n.[[[[[[ 784783012 ]]]]]无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!希望新手少走弯路 ...

December 2, 2018 · 1 min · jiezi

CSS 盒子模型及 float 和 position

CSS盒模型CSS盒模型本质上是一个盒子,封装周围的 HTML 元素,包括 外边距(marign),边框(border),填充(padding),内容物(content)盒子模型的类型:W3C 标准和模型和 IE 盒模型(怪异盒模型)W3C 标准盒模型:属性 width 和 height 只包含 content,不包括 border 和 paddingIE 盒模型:属性 width 和 height 包含 border 和 padding,指的是 content + padding + bordercss3新增的 box-sizing 属性的值 content-box 就是标准盒模型,border-box 就是 IE 盒模型盒模型 – 基本框CSS 假定每个元素都会生成一个或多个矩形框,各元素框中心又一个内容区,这个内容区周围有可选的内边距、边框和外边距。盒模型 – 包含块每个元素都相对于其包含块摆放,包含块就是一个元素的布局上下文。盒模型 – 正常流指的是西方语言文本从左向右、从上向下显示,这也是传统 HTML 文档的文本布局。大多数元素都是在正常流中,要让一个元素不在正常流中,唯一的办法就是使之成为浮动或定位元素。块级元素控制盒加粗文字模型的水平方向的有 7 个值:margin-left、border-left、padding-left、width、padding-right、border-right、margin-right(加在一起必须等于包含快的宽度)。其中 margin 和 width 可以设置为 auto,其他的要么是确定的值,要么就是默认的 0。margin 值可以设置为负,padding 不可以。水平外边距不会合并。一个元素的默认高度是由其内容确定,可以对任何块级元素设置显示高度。同样,控制盒模型的竖直方向的有 7 个值:margin-top、border-top、padding-top、height、padding-top、border-top、margin-top(加在一起必须等于包含快的高度)。其中 margin 和 height 可以设为 auto,但是上下外边距设置为 auto 没什么用,因为会被重置为 0。如果块级正常流的高度设为 auto,而且只有块级子元素,其默认高度是从最高块级子元素的外边框边界到最低块级子元素外边框边界之间的距离。垂直方向上另一个重要的方面是:相邻的元素外边距会合并。如果相邻两个元素垂直外边距都设置为负值,浏览器会取绝对值大的作为外边距。如果一正一负,会取正外边距减去负外边距的绝对值的差作为外边距。block、inline、inline-block 对比block:元素独占一行,默认情况下,元素宽度自动填满父元素宽度。可以设置 width、height 属性,设置了宽高的块级元素仍独占一行。块级元素可以设置 margin 和 padding.inline:元素不会独占一行,多个相邻的元素会在一行排列,排列不下会自动换行,其宽度随元素内容而变化。内联元素设置 width、height 无效。内联元素的 margin 和 padding 属性,水平方向有效,竖直方向不会产生边距效果。inline-block:将对象呈现为内联对象,但是对象的内容具有块级元素的属性。float 与 position 的区别文档流:在 css 中有一个 z-index 属性,默认情况下,所有页面元素均位于 z-index: 0 这一层,而这一层顺序排列的元素就叫文档流。float 和 position 都是通过改变文档流来实现定位。CSS 有 三种定位机制:文档流、浮动和绝对定位。除非专门指定,否则所有元素都在文档流中定位。CSS 定位的基本思想很简单,它允许你使得元素相对于其正常应该出现的位置,或者相对于父元素、另一个元素甚至浏览器窗口本身的位置来进行定位。floatfloat 属性定位的元素位于 z-index: 0 层,它是通过 float: left 和 float: right 来控制元素在 0层左浮还是右浮,float会改变整成的文档流排列,影响到周围的元素,但是不会脱离文档流。float 元素在文档流中一个挨着一个排列,但只是float 元素之间一个挨着一个排列,对于非 float 元素,float 元素会越过它们,即 float: left 会把非 float的元素挤到所有 float 的元素右边,float: right 则挤到左边。positionpostion 属性有四个值:static(默认值)、relative、absolute、fixed1.static:静态定位。元素框正常生成,元素顺序显示,在一个文档流中,一个挨着一个,内容遵守正常从上到下的 HTML 流。2.relative:相对定位。元素偏移某个距离。元素仍保持其未定位前的框的形状,它原本所占的空间仍保留。一个相对定位的元素相对它在 HTML 流中当前位置而放置。相对定位的主要用处不是移动一个元素,而是给行内在它内部的绝对定位的元素设定一个新的参考点。position: relative 的元素是相对于自己本来应该在的位置进行偏移。偏移后的位置是浮在上方的,但仍会保留自己在 z-index: 0 中的位置,不会影响相邻元素。absolute:绝对定位。绝对定位可以通过 px、em、% 来指定一个左、右、上、下的位置来确定一个元素的位置。此外,绝对定位的元素被完全与页面流分离。3.absolute 的一般用法:如果一个元素被设定为绝对定位,并且不在任何其他应用了 absolute、relative、fixed 定位的标签里,那么它是相对于页面(body 元素)进行定位的。如果一个元素在另一个带有 absolute、relative 或者 fixed 定位的标签里,那么它是相对于该元素的边界进行定位的。即:position: absolute 的元素在 static 的父元素中是相对页面进行偏移的,在非 static 的父元素中,是相对父元素进行偏移的。4.fixed:一个元素被固定定位在屏幕的某个位置上,是相对浏览器窗口进行定位的。如果你渴望学习,希望变强,可以来我的前端学习q..u.n.: 784783012 友都会在里面交流,分享一些学习的方法和需要注意的小细节,每天也会准时的讲一些前端的炫酷特效 ...

December 2, 2018 · 1 min · jiezi

前端每日实战:163# 视频演示如何用原生 JS 和 GSAP 创作一个多选一场景的交互游戏(内含 3 个视频)

效果预览按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。https://codepen.io/comehope/pen/LXMzRX可交互视频此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。请用 chrome, safari, edge 打开观看。第 1 部分:https://scrimba.com/p/pEgDAM/cQK3bSp第 2 部分:https://scrimba.com/p/pEgDAM/cNJWncR第 3 部分:https://scrimba.com/p/pEgDAM/cvgP8td源代码下载每日前端实战系列的全部源代码请从 github 下载:https://github.com/comehope/front-end-daily-challenges代码解读多选一的场景是很常见的,浏览器自带的 <input type=“radio”> 控件就适用于这样的场景。本项目将设计一个多选一的交互场景,用 css 进行页面布局、用 gsap 制作动画效果、用原生 js 编写程序逻辑。这个游戏的逻辑很简单,在页面上部展示出一个动物的全身像,请用户在下面的小图片中选择这个动物对应的头像,如果选对了,就可以再玩一次。整个游戏分成 3 个步骤开发:静态的页面布局、程序逻辑和动画效果。一、页面布局定义 dom 结构,容器中包含标题 h1、全身像 .whole-body、当选择正确时的提示语 .bingo、“再玩一次”按钮 .again、一组选择按钮 .selector。.selector 中包含 5 个展示头像的 .face 和 1 个标明当前被选中头像的 .slider。全身像和头像没有使用图片,都用 unicode 字符代替:<div class=“app”> <h1>Which face is the animal’s?</h1> <div class=“whole-body”>????</div> <div class=“bingo”> Bingo! <span class=“again”>Play Again</span> </div> <div class=“selector”> <span class=“slider”></span> <span class=“face”>????</span> <span class=“face”>????</span> <span class=“face”>????</span> <span class=“face”>????</span> <span class=“face”>????</span> </div> </div>居中显示:body { margin: 0; height: 100vh; display: flex; align-items: center; justify-content: center; background: linear-gradient(darkblue, black);}定义容器中子元素的按纵向布局,水平居中:.app { height: 420px; display: flex; flex-direction: column; align-items: center; justify-content: space-between;}标题是白色文字:h1 { margin: 0; color: white;}全身像为大尺寸的圆形,利用阴影画一个半透明的粗边框:.whole-body { width: 200px; height: 200px; background-color: rgb(180, 220, 255); border-radius: 50%; font-size: 140px; text-align: center; line-height: 210px; margin-top: 20px; box-shadow: 0 0 0 15px rgba(180, 220, 255, 0.2); user-select: none;}选择正确时的提示语为白色:.bingo { color: white; font-size: 30px; font-family: sans-serif; margin-top: 20px;}“再玩一次”按钮的字体稍小,在鼠标悬停和点击时有交互效果:.again { display: inline-block; font-size: 20px; background-color: white; color: darkblue; padding: 5px; border-radius: 5px; box-shadow: 5px 5px 2px rgba(0, 0, 0, 0.4); user-select: none;}.again:hover { background-color: rgba(255, 255, 255, 0.8); cursor: pointer;}.again:active { transform: translate(2px, 2px); box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.4);}5 个头像为小尺寸的圆形,横向排列,半透明背景:.selector { display: flex;}.face { width: 60px; height: 60px; background-color: rgba(255, 255, 255, 0.2); border-radius: 50%; font-size: 40px; text-align: center; line-height: 70px; cursor: pointer; user-select: none;}.face:not(:last-child) { margin-right: 25px;}在被选中的头像下面叠加一个同尺寸的浅蓝色色块:.selector { position: relative;}.slider { position: absolute; width: 60px; height: 60px; background-color: rgba(180, 220, 255, 0.6); border-radius: 50%; z-index: -1;}至此,页面布局完成。二、程序逻辑引入 lodash 工具库,后面会用到 lodash 提供的一些数组函数:<script src=“https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>在写程序逻辑之前,我们先定义 2 个常量。第一个常量是存储动物头像和全身像的数据对象 animals,它的每个属性是 1 种动物,key 是头像,value 是全身像:const animals = { ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’,}第二个常量是存储 dom 元素引用的数据对象 dom,它的每个属性是一个 dom 元素,key 值与 class 类名保持一致,分别是代表全身像的 dom.wholeBody、代表选择正确时的提示信息 dom.bingo、代表“再玩一次”按钮的 dom.bingo、代表头像列表的 dom.faces、代表头像下面的滑块 dom.slider:const dom = { wholeBody: document.querySelector(’.whole-body’), bingo: document.querySelector(’.bingo’), again: document.querySelector(’.again’), faces: Array.from(document.querySelectorAll(’.face’)), slider: document.querySelector(’.slider’),}接下来定义整体的逻辑结构,当页面加载完成之后执行 init() 函数,init() 函数会对整个游戏做些初始化的工作 ———— 令头像 dom.faces 被点击时调用 select() 函数,令“再玩一次”按钮 dom.again 被点击时调用 newGame() 函数 ———— 最后调用 newGame() 函数开始一局新游戏:function newGame() { //…}function select() { //…}function init() { dom.faces.forEach(face => { face.addEventListener(‘click’, select) }) dom.again.addEventListener(‘click’, newGame) newGame()}window.onload = init在 newGame() 函数中调用 shuffle() 函数。shuffle() 函数的作用是随机地从 animals 数组中选出 5 个动物,把它们的头像显示在 dom.faces 中,再从中选出 1 个动物,把它的全身像显示在 dom.wholeBody 中。变量 options 代表被选出的 5 个动物,变量 answer 代表显示全身像的动物,因为后面还会用到 options 和 answer,所以把它们定义为全局变量。经过 .entries() 函数的处理,options 数组的元素和 answer 的数据结构变为包含 2 个元素的数组 [key, value] 形式,其中第 [0] 个元素是头像,第 [1] 个元素是全身像:let options = []let answer = {}function newGame() { shuffle()}function shuffle() { options = .slice(.shuffle(.entries(animals)), -5) answer = .sample(.slice(options, -4)) dom.faces.forEach((face, i) => { face.innerText = options[i][0] }) dom.wholeBody.innerText = answer[1]}现在,每点击一次 Play Again 按钮,就会洗牌、更新图片。接下来处理滑块。在 select() 函数中,首先把滑块 dom.slider 移动到被点击的头像位置:function select(e) { let position = _.findIndex(options, (o) => o[0] == e.target.innerText) dom.slider.style.left = (25 + 60) * position + ‘px’}然后判断当前头像对应的全身像和页面上方全身像是否一致,若一致,就显示提示语 dom.bingo。在此之前,要把提示语隐藏掉:function newGame() { dom.bingo.style.visibility = ‘hidden’ shuffle()}function select(e) { let position = _.findIndex(options, (o) => o[0] == e.target.innerText) dom.slider.style.left = (25 + 60) * position + ‘px’ if (animals[e.target.innerText] == answer[1]) { dom.bingo.style.visibility = ‘visible’ }}现在,游戏开局时是没有提示语的,只有选对了头像,才会出提示语。不过出现了一个bug,就是当重开新局时,滑块还停留在上一局的位置,我们要改成开局时把滑块 dom.slider 移到头像列表的最左侧:function newGame() { dom.bingo.style.visibility = ‘hidden’ shuffle() dom.slider.style.left = ‘0px’}现在,整个程序流程已经可以跑通了:页面加载后即开始一局游戏,任意选择头像,在选择了正确的头像时出现 Bingo! 字样,点击 Play Again 按钮可以开始下一局游戏。不过,在逻辑上还有一点小瑕疵。当用户已经选择了正确的头像,显示出提示语之后,不应该还能点选其他头像。为此,我们引入一个全局变量 canSelect,它是一个布尔值,表示当前是否可以选择头像,初始值是 false,在 newGame() 函数的最后一步,它的值被设置为 true,在 select() 函数中首先判断 canSelect 的值,只有当值为 true 时,才能继续执行事件处理的后续程序,当用户选择了正确的头像时,canSelect 被设置为 false,表示这一局游戏结束了。let canSelect = falsefunction newGame() { dom.bingo.style.visibility = ‘hidden’ shuffle() dom.slider.style.left = ‘0px’ canSelect = true}async function select(e) { if (!canSelect) return; let position = .findIndex(options, (o) => o[0] == e.target.innerText) await animation.moveSlider(position) if (animals[e.target.innerText] == answer[1]) { canSelect = false await animation.showBingo() }}至此的全部脚本如下:const animals = { ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’, ‘????’: ‘????’,}const dom = { wholeBody: document.querySelector(’.whole-body’), bingo: document.querySelector(’.bingo’), again: document.querySelector(’.again’), faces: Array.from(document.querySelectorAll(’.face’)), slider: document.querySelector(’.slider’),}let options = []let answer = {}let canSelect = falsefunction newGame() { dom.bingo.style.visibility = ‘hidden’ shuffle() dom.slider.style.left = ‘0px’ canSelect = true}function shuffle() { options = .slice(.shuffle(.entries(animals)), -5) answer = .sample(.slice(options, -4)) dom.faces.forEach((face, i) => { face.innerText = options[i][0] }) dom.wholeBody.innerText = answer[1]}function select(e) { if (!canSelect) return; let position = _.findIndex(options, x => x[0] == e.target.innerText) dom.slider.style.left = (25 + 60) * position + ‘px’ if (animals[e.target.innerText] == answer[1]) { canSelect = false dom.bingo.style.visibility = ‘visible’ }}function init() { dom.faces.forEach(face => { face.addEventListener(‘click’, select) }) dom.again.addEventListener(‘click’, newGame) newGame()}window.onload = init三、动画效果游戏中共有 4 个动画效果,分别是移动滑块 dom.slider、显示提示语 dom.bingo、动物(包括头像列表和全身像)出场、动物入场。为了集中管理动画效果,我们定义一个全局常量 animation,它有 4 个属性,每个属性是一个函数,实现一个动画效果,结构如下:const animation = { moveSlider: () => { //移动滑块… }, showBingo: () => { //显示提示语… }, frameOut: () => { //动物出场… }, frameIn: () => { //动物入场… },}其实这 4 个动画的运行时机已经体现在 newGame() 函数和 select() 函数中了:function newGame() { dom.bingo.style.visibility = ‘hidden’ //此处改为 动物出场 动画 shuffle() dom.slider.style.left = ‘0px’ //此处改为 动物入场 动画 canSelect = true}function select(e) { if (!canSelect) return; let position = _.findIndex(options, (o) => o[0] == e.target.innerText) dom.slider.style.left = (25 + 60) * position + ‘px’ //此处改为 移动滑块 动画 if (animals[e.target.innerText] == answer[1]) { canSelect = false dom.bingo.style.visibility = ‘visible’ //此处改为 显示提示语 动画 }}所以,我们就可以把这 4 行代码转移到 animation 中,其中 moveSlider() 还增加了一个指明要移动到什么位置的 position 参数:const animation = { moveSlider: (position) => { dom.slider.style.left = (25 + 60) * position + ‘px’ }, showBingo: () => { dom.bingo.style.visibility = ‘visible’ }, frameOut: () => { dom.bingo.style.visibility = ‘hidden’ }, frameIn: () => { dom.slider.style.left = ‘0px’ },}同时,newGame() 函数和 select() 函数改为调用 animation:function newGame() { animation.frameOut() shuffle() animation.frameIn() canSelect = true}function select(e) { if (!canSelect) return; let position = _.findIndex(options, (o) => o[0] == e.target.innerText) animation.moveSlider(position) if (animals[e.target.innerText] == answer[1]) { canSelect = false animation.showBingo() }}经过上面的整理,接下来的动画代码就可以集中写在 animation 对象里了。本项目的动画效果用 gsap 实现,gsap 动画在以前的 133#项目、134#项目、143#项目 都用到了,大家可参考这些项目了解 gsap 的使用方法。引入 gsap 动画库:<script src=“https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script>先编写移动滑块的动画 moveSlider,让滑块先缩小,然后移动到目的地,再放大:const animation = { moveSlider: () => { new TimelineMax() .to(dom.slider, 1, {scale: 0.3}) .to(dom.slider, 1, {left: (25 + 60) * position + ‘px’}) .to(dom.slider, 1, {scale: 1}) .timeScale(5) }, //…}再编写显示提示语的动画 showBingo,显示出 dom.bingo 之后,让它左右晃动一下:const animation = { //… showBingo: () => { new TimelineMax() .to(dom.bingo, 0, {visibility: ‘visible’}) .to(dom.bingo, 1, {rotation: -5}) .to(dom.bingo, 1, {rotation: 5}) .to(dom.bingo, 1, {rotation: 0}) .timeScale(8) }, //…}再编写动物出场的动画,隐藏提示语 dom.bingo 之后,再同时把滑块 dom.slider、头像列表 dom.faces、全身像 dom.wholeBody 同时缩小到消失:const animation = { //… frameOut: () => { new TimelineMax() .to(dom.bingo, 0, {visibility: ‘hidden’}) .to(dom.slider, 1, {scale: 0}, ’t1’) .staggerTo(dom.faces, 1, {scale: 0}, 0.25, ’t1’) .to(dom.wholeBody, 1, {scale: 0}, ’t1’) .timeScale(5) }, //…}再编写动物入场的动画,把滑块移到头像列表最左侧之后,再把刚才出场动画缩小到消失的那些元素放大到正常尺寸:const animation = { //… frameIn: () => { new TimelineMax() .to(dom.slider, 0, {left: ‘0px’}) .to(dom.wholeBody, 2, {scale: 1, delay: 1}) .staggerTo(dom.faces, 1, {scale: 1}, 0.25) .to(dom.slider, 1, {scale: 1}) .timeScale(5) },}现在运行一下程序,已经有动画效果了,但是会觉得有些不协调,那是因为动画有一定的运行时长,多个动画连续运行时应该有先后顺序,比如应该先出场再入场、先移动滑块再显示提示语,但现在它们都是同时运行的。为了让它们能顺序执行,我们用 async/await 来改造,先让动画函数返回 promise 对象,以 moveSlider 为例,它被改成这样:const animation = { moveSlider: () => { return new Promise(resolve => { new TimelineMax() .to(dom.slider, 1, {scale: 0.3}) .to(dom.slider, 1, {left: (25 + 60) * position + ‘px’}) .to(dom.slider, 1, {scale: 1}) .timeScale(5) .eventCallback(‘onComplete’, resolve) }) }, //…}然后把 select() 函数改造成 async 函数,并在调用动画之前加入 await 关键字:async function select(e) { if (!canSelect) return; let position = _.findIndex(options, (o) => o[0] == e.target.innerText) await animation.moveSlider(position) if (animals[e.target.innerText] == answer[1]) { canSelect = false animation.showBingo() }}现在点击头像时,若选择正确,要等到滑块动画结束之后才会显示提示语。再用相同的方法,改造其他几个动画和 select() 函数。到这里,整个游戏的动画效果就全部完成了。至此的全部脚本如下:const animals = { //略,与增加动画前相同}const dom = { //略,与增加动画前相同}const animation = { frameOut: () => { return new Promise(resolve => { new TimelineMax() .to(dom.bingo, 0, {visibility: ‘hidden’}) .to(dom.slider, 1, {scale: 0}, ’t1’) .staggerTo(dom.faces, 1, {scale: 0}, 0.25, ’t1’) .to(dom.wholeBody, 1, {scale: 0}, ’t1’) .timeScale(5) .eventCallback(‘onComplete’, resolve) }) }, frameIn: () => { return new Promise(resolve => { new TimelineMax() .to(dom.slider, 0, {left: ‘0px’}) .to(dom.wholeBody, 2, {scale: 1, delay: 1}) .staggerTo(dom.faces, 1, {scale: 1}, 0.25) .to(dom.slider, 1, {scale: 1}) .timeScale(5) .eventCallback(‘onComplete’, resolve) }) }, moveSlider: (position) => { return new Promise(resolve => { new TimelineMax() .to(dom.slider, 1, {scale: 0.3}) .to(dom.slider, 1, {left: (25 + 60) * position + ‘px’}) .to(dom.slider, 1, {scale: 1}) .timeScale(5) .eventCallback(‘onComplete’, resolve) }) }, showBingo: () => { return new Promise(resolve => { new TimelineMax() .to(dom.bingo, 0, {visibility: ‘visible’}) .to(dom.bingo, 1, {rotation: -5}) .to(dom.bingo, 1, {rotation: 5}) .to(dom.bingo, 1, {rotation: 0}) .timeScale(8) .eventCallback(‘onComplete’, resolve) }) },}let options = []let answer = {}let canSelect = falseasync function newGame() { await animation.frameOut() shuffle() await animation.frameIn() canSelect = true}function shuffle() { //略,与增加动画前相同}async function select(e) { if (!canSelect) return; let position = _.findIndex(options, (o) => o[0] == e.target.innerText) await animation.moveSlider(position) if (animals[e.target.innerText] == answer[1]) { canSelect = false await animation.showBingo() }}function init() { //略,与增加动画前相同}window.onload = init最后,附上程序流程图,方便大家理解。其中蓝色条带表示动画,粉色椭圆表示用户操作,绿色矩形和菱形表示主要的程序逻辑,橙色平等四边形表示 canSelect 变量。大功告成! ...

November 30, 2018 · 6 min · jiezi

【译】只用 CSS 就能做到的像素画/像素动画

只用 CSS 就能做到的像素画/像素动画原文链接:box-shadowを使ってCSSだけでドット絵を描き、アニメーションさせる作者推特:bc_rikko作者的推特里面有不少例子,有能力的同学可以看一下这篇文章将会介绍只用 CSS 就能制作像素画·像素动画的方法。虽说纯 CSS 就能做到,但是为了更高的可维护性,也会顺便介绍使用 Sass 的制作方法。上面的马里奥和 Minecraft 方块都没有使用 JavaScript,单纯使用 CSS 动画制作。关于 box-shadow 属性绘制像素点可以借助 box-shadow 属性。原本 box-shadow 属性用于制作阴影效果,先介绍一下基本用法。该属性的写法有几种:box-shadow: offset-x offset-y colorbox-shadow: offset-x offset-y blur-radius colorbox-shadow: offset-x offset-y blur-radius spread-radius colorbox-shadow: inset offset-x offset-y coloroffset-x 和 offset-y 用于指定阴影偏移位置。以元素的左上角为原点,指定 XY 轴移动的位置。color 字面意思,指定阴影颜色。blur-radius 指定模糊效果的半径。跟 border-radius 差不多。spread-raduis 模糊范围的扩大与缩小。inset 关键字可以使阴影效果显示在元素内则。文字说明或许不够形象,我们可以直接看效果:https://jsfiddle.net/bc_rikko…实际效果如下,每个值会造成什么影响应该能很直观地看懂。基础:描绘一个像素点box-shadow 基础都明白了,就可以进入下一步:描绘一个像素点。对一个边长 100px 的正方形使用 box-shadow:<div class=“container”> <div class=“box”></div></div><style>* { /* 为了方便看到元素而添加的边框(不加也行) / box-sizing: border-box;}.container { / 长和宽包括 box-shadow / width: 200px; height: 200px;}.box { / 元素属性 / width: 100px; height: 100px; border: 2px solid #777; / 在元素右下角相同大小的方块 / box-shadow: 100px 100px rgba(7,7,7,.3);}</style>如图所示,使用 box-shadow 描绘了一个与元素相同大小的阴影。代码的意思是把一个 100px 的方形的影子放到 (100px, 100px) 的位置。进阶:用 box-shadow 属性绘制像素画完成预想图这两个都是 5✖️5 的像素画,我们先从左边开始:<div class=“container”> <div class=“pixel one”></div></div><style>.container { / 像素画的大小 / width: 100px; height: 100px;}.pixel { / 使伪元素的位置可调整 / position: relative;}.pixel::before { content: “”; / 一个点的大小(例:20px x 20px) / width: 20px; height: 20px; / box-shadow 着色,伪元素设为透明 / background-color: transparent; / 调整伪元素位置,让左上角成为(0,0) / position: absolute; top: -20px; left: -20px;}.pixel.one::before { box-shadow: / 列 行 色 / / 第1列 / 20px 20px #FB0600, 20px 40px #FC322F, 20px 60px #FC6663, 20px 80px #FD9999, 20px 100px #FECCCB, / 第2列 / 40px 20px #60169F, 40px 40px #7A23B0, 40px 60px #964DC2, 40px 80px #B681D9, 40px 100px #D8BEED, / 第3列 / 60px 20px #1388BC, 60px 40px #269DC9, 60px 60px #55B3D7, 60px 80px #88CAE2, 60px 100px #BFE3EF, / 第4列 / 80px 20px #ACD902, 80px 40px #BDE02D, 80px 60px #CDEA5E, 80px 80px #DBEF8E, 80px 100px #F4FBC8, / 第5列 / 100px 20px #FB8F02, 100px 40px #FDA533, 100px 60px #FDBB64, 100px 80px #FED39A, 100px 100px #FDE8C9;}</style>首先,box-shadow 生产的影子大小不包括本体元素的大小,container 类的大小设为像素画完成后的大小就行。接着,box-shadow 的影子大小由,pixel 类的大小决定,所以把 width 和 height设定为 20px。实际的点是 before 伪元素绘制的,pixel 的 20px 正方形会在左上角留下空位,为此可以使用 position: absolute 调整。最后使用 box-shadow 逐格绘制像素画。接着实现右边的像素画。.pixel.two::before { box-shadow: 20px 20px #704b16, 40px 20px #704b16, 60px 20px #704b16, 80px 20px #704b16, 100px 20px #704b16, 20px 40px #704b16, 40px 40px #fdb778, 60px 40px #fdb778, 80px 40px #fdb778, 100px 40px #704b16, 20px 60px #fdb778, 40px 60px #333333, 60px 60px #fdb778, 80px 60px #333333, 100px 60px #fdb778, 20px 80px #fdb778, 40px 80px #fdb778, 60px 80px #fdb778, 80px 80px #fdb778, 100px 80px #fdb778, 20px 100px #fdb778, 40px 100px #c70300, 60px 100px #c70300, 80px 100px #c70300, 100px 100px #fdb778;}应用:使用 Sass 编写可维护像素画上面写的几个例子,至少我是没什么信心去维护好他们。5x5 的像素画要写 25 次属性值,一般的 16x16 则是多达 256 个值。所以,我们可以使用 Sass 编写可维护像素画。Sass 环境搭建可以参考以下文章(日语)https://kuroeveryday.blogspot…Sass 使用 mixin(function 亦可)生成样式的方法:@mixin pixelize($matrix, $size, $colors) { $ret: “”; @for $i from 1 through length($matrix) { $row: nth($matrix, $i); @for $j from 1 through length($row) { $dot: nth($row, $j); @if $dot != 0 { @if $ret != "" { $ret: $ret + “,”; } $color: nth($colors, $dot); $ret: $ret + ($j * $size) + " " + ($i * $size) + " " + $color; } } } box-shadow: unquote($ret + “;”);}$heart-colors: (#333, #f11416, #831200);$heart: ( (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), (0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0), (0,1,2,2,2,1,0,0,0,1,2,2,3,1,0,0), (1,2,0,0,2,2,1,0,1,2,2,2,2,3,1,0), (1,2,0,2,2,2,2,1,2,2,2,2,2,3,1,0), (1,2,2,2,2,2,2,2,2,2,2,2,2,3,1,0), (1,2,2,2,2,2,2,2,2,2,2,2,2,3,1,0), (1,2,2,2,2,2,2,2,2,2,2,2,2,3,1,0), (0,1,2,2,2,2,2,2,2,2,2,2,3,1,0,0), (0,0,1,2,2,2,2,2,2,2,2,3,1,0,0,0), (0,0,0,1,2,2,2,2,2,2,3,1,0,0,0,0), (0,0,0,0,1,2,2,2,2,3,1,0,0,0,0,0), (0,0,0,0,0,1,2,2,3,1,0,0,0,0,0,0), (0,0,0,0,0,0,1,3,1,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0), (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0));.icon { width: 20px; height: 20px; @include pixelize($heart, 20px, $heart-colors);}定义名为 pixelize 的 mixin,把像素画的矩阵($heart)像素点的大小(20px)颜色列表($hearts-colors)传入其中,即可生成 box-shadow 属性。像素画的矩阵用数字 0~N 表示,0 为透明,1~n 为颜色列表对应颜色。如果有代码高亮的话,像素画的图案就一目了然啦。与原生 CSS 相比,这样简单多了吧?如果这样都觉得麻烦,可以使用 CSS 像素画生成器~CSSドット絵ジェネレータ番外篇:制作像素动画之前 icon 类直接使用 box-shadow 属性绘制像素画,在制作像素动画时,需要使用 CSS animation。.mario { width: 8px; height: 8px; animation: jump 1s infinite, sprite 1s infinite;}/ 跳跃动作(上下移動) /@keyframes jump { from, 25%, 75%, to { transform: translateY(0); } 50% { transform: translateY(calc(8px * -8)); }}/ 普通状态和跳跃状态的像素画 /@keyframes sprite { / 对比 animation-timing-function: steps(n) * 使用百分比可以更细致的调整动画时间 / from, 24%, 76%, to { box-shadow: / 普通状态的像素画 / } 25%, 75% { box-shadow: / 跳跃状态的像素画 */ }}使用 CSS 动画修改 box-shadow 和元素的位置,看起来就像是跳起来一样。详细代码可以在 github 仓库中了解https://github.com/BcRikko/cs… ...

November 29, 2018 · 3 min · jiezi

CSS3 入门详解(二)

动画动画是CSS3中具有颠覆性的特征之一,可通过设置多个节点来精确控制一个或一组动画,常用来实现复杂的动画效果。11.1 如何实现动画必要元素:a、通过@keyframes指定动画序列;b、通过百分比将动画序列分割成多个节点;c、在各节点中分别定义各属性d、通过animation将动画应用于相应元素;示例代码: 两种定义动画的方式<style> /* move 是定义的动画名 / @keyframes move { 0% { transform: translateX(0px); width: 200px; height: 200px; } 40% { width: 300px; height: 300px; } 80% { width: 200px; height: 200px; transform: translateX(100px); } 100% { transform: translateX(0px); width: 200px; height: 200px; } } / 定义动画集 / @keyframes move1 { from { / 动画的开始状态 / / 位置开始状态 / transform: translateX(0px); } to { / 动画的结束状态 / / 位置结束状态 / transform: translateX(300px); } } .box { width: 200px; height: 200px; background-color: pink; / 调用动画集 动画名 持续时间 执行速度 / animation: move 5s linear; }</style>11.2 动画关键属性关键属性a、animation-name设置动画序列名称b、animation-duration动画持续时间c、animation-delay动画延时时间d、animation-timing-function动画执行速度,linear、ease等e、animation-play-state动画播放状态,running、paused等f、animation-direction动画逆播,alternate等g、animation-fill-mode动画执行完毕后状态,forwards、backwards等h、animation-iteration-count动画执行次数,inifinate等i、steps(60)表示动画分成60步完成参数值的顺序:关于几个值,除了名字,动画时间,延时有严格顺序要求其它随意。示例代码:<style type=“text/css”> .box { width: 200px; height: 200px; background-color: pink; / 调用动画集 / animation: move 5s linear; } / 定义动画集 / @keyframes move { 0% { / 位置开始状态 / transform: translateX(0px); / 动画的开始状态 / width: 100px; height: 100px; } 40% { width: 150px; height: 150px; background: orange; } 80% { width: 200px; height: 200px; background: cyan; transform: translateX(100px); } 100% { / 位置结束状态 / transform: translateX(0px); / 动画的结束状态 / width: 100px; height: 100px; background: pink; } }</style><div class=“box”></div>效果图:11.3 动画案例1、全屏切换定义三个动画集,当点击a标签的时候,通过他的锚点触发对应的动画集。<style type=“text/css”> * { margin: 0; padding: 0; } html,body { width: 100%; height: 100%; } .box { width: 100%; height: 100%; position: relative; overflow: hidden; } .box>div{ width: 100%; height: 100%; position: absolute; background-size: cover; } .one { background: url(img/bg6.jpg) no-repeat; } .two { background: url(img/bg7.jpg) no-repeat; } .three { background: url(img/bg8.jpg) no-repeat; } .box .numbers { width: 150px; height: 40px; left: 50%; margin-left: -75px; bottom: 30px; z-index: 4; } .box .numbers a { width: 40px; height: 40px; display: block; line-height: 40px; text-align: center; text-decoration: none; color: #777; background-color: rgba(255,255,255,.8); border-radius: 50%; float: left; margin: 0 5px; } .one:target { animation: move 1s linear; z-index: 1; } .two:target { animation: rotate 1s linear; z-index: 1; } .three:target { animation: scale 1s linear; z-index: 1; } @keyframes move { from { transform: translateX(-100%); } to { transform: translateX(0px); } } @keyframes rotate { from { transform: scale(0) rotateZ(0deg); } to { transform: scale(1) rotateZ(360deg); } } @keyframes scale { from { transform: scale(0); } to { transform: scale(1); } }</style><div class=“box”> <div class=“one” id=“one”></div> <div class=“two” id=“two”></div> <div class=“three” id=“three”></div> <div class=“numbers”> <a href="#one">1</a> <a href="#two">2</a> <a href="#three">3</a> </div></div>效果图:2、大海波涛波浪其实是两张图片,执行的是同一组动画,只需要在第二张图片执行动画的时候,让它延迟1s执行,就可以出现“波动”的感觉。<style type=“text/css”> * { margin: 0; padding: 0; } html,body { width: 100%; height: 100%; } .box { width: 100%; height: 100%; background-color: #0EA9B1; position: relative; overflow: hidden; } .box img { position: absolute; width: 100%; bottom: 0; } .sun { width: 80px; height: 80px; background-color: #fff; border-radius: 50%; position: absolute; left: 100px; top: 50px; } .sun:after { content: “”; display: block; width: 100px; height: 100px; border-radius: 50%; background-color: rgba(255,255,255,.6); position: absolute; left: 50%; top: 50%; margin-left: -50px; margin-top: -50px; animation: scale 1s linear infinite alternate; } .sun:before { content: “”; display: block; width: 120px; height: 120px; border-radius: 50%; background-color: rgba(255,255,255,.4); position: absolute; left: 50%; top: 50%; margin-left: -60px; margin-top: -60px; animation: scale 2s 1s linear infinite alternate; } @keyframes scale { from { transform: scale(1); } to { transform: scale(1.2); } } @keyframes move{ from { bottom: 0px; } to { bottom: -60px; } } img:nth-child(2){ animation: move 1s linear infinite alternate; } img:nth-child(3) { animation: move 2s 1s linear infinite alternate; }</style> <div class=“box”> <div class=“sun”></div> <img src=“img/1.png” alt=""> <img src=“img/2.png” alt=""> </div>效果图:3、宇宙通过定位确定各“星球”的位置,再对每个星球设定单独的动画,形成联动的各大星球。<style type=“text/css”> * { margin: 0; padding: 0; } html,body { width: 100%; height: 100%; background: url(img/bg.jpg) no-repeat; background-size: cover; position: relative; } .box { width: 600px; height: 600px; border: 1px solid #ccc; border-radius: 50%; position: absolute; left: 50%; margin-left: -300px; top: 50%; margin-top: -300px; } .sun { position: absolute; width: 50px; height: 50px; background-color: orange; border-radius: 50%; margin-left: -25px; left: 50%; margin-top: -25px; top: 50%; box-shadow: 0px 0px 50px orange; } .lin1 { width: 100px; height: 100px; border: 1px solid #ccc; border-radius: 50%; margin-left: -50px; margin-top: -50px; animation: rotate 10s linear infinite; } .lin1:after { content: “”; width: 20px; height: 20px; display: block; border-radius: 50%; background-color: orange; position: absolute; left: -2px; top: 10px; } .public { position: absolute; left: 50%; top: 50%; } @keyframes rotate { from { transform: rotateZ(0deg); } to { transform: rotateZ(360deg); } } .lin2 { width: 150px; height: 150px; border: 1px solid #ccc; border-radius: 50%; margin-left: -75px; margin-top: -75px; animation: rotate 5s linear infinite; } .lin2:after { content: “”; width: 10px; height: 10px; background-color: blue; display: block; border-radius: 50%; position: absolute; left: 25px; top: 10px; } .lin3 { width: 230px; height: 230px; border: 1px solid #ccc; border-radius: 50%; margin-left: -115px; margin-top: -115px; animation: rotate 10s linear infinite; } .lin3 span { width: 20px; height: 20px; display: block; background-color: green; border-radius: 50%; position: absolute; left: 50px; animation: rotate 5s linear infinite; } .lin3 span b { width: 10px; height: 10px; background-color: pink; display: block; border-radius: 50%; position: absolute; left: 25px; } .lin4 { width: 332px; height: 332px; border: 1px solid #ccc; background: url(img/asteroids_meteorids.png) no-repeat; border-radius: 50%; margin-left: -161px; margin-top: -161px; animation: rotate 15s linear infinite; } .lin4:after { content: “”; display: block; width: 10px; height: 10px; background-color: red; border-radius: 50%; position: absolute; left: 120px; } .line5 { width: 450px; height: 450px; border: 1px solid #ccc; border-radius: 50%; margin-left: -220px; margin-top: -220px; animation: rotate 10s linear infinite; } .line5 span { width: 30px; height: 30px; background-color: blue; border-radius: 50%; display: block; position: absolute; left: 120px; } .line5 span b { display: block; width: 40px; height: 40px; border: 5px solid #ccc; border-radius: 50%; transform: skew(45deg); position: absolute; left: -6px; top: -10px; }</style> <div class=“box”> <div class=“sun”></div> <div class=“lin1 public”></div> <div class=“lin2 public”></div> <div class=“lin3 public”> <span> <b></b> </span> </div> <div class=“lin4 public”></div> <div class=“line5 public”> <span> <b></b> </span> </div></div>效果图:4、无缝滚动轮播结构上与普通的轮播图没有什么区别,定义一个盒子的宽度,宽度为八张图片的总宽度,定义一个动画集,10s内将ul从0的位置移动到盒子的宽度,此时动画集结束,应该是跳到0的位置,循环此动画就会形成无缝滚动。之所以ul下的li定义的是十四张图片,是因为,一开始的时候图片从零的位置向右移动时,如果只有七张图片的时候,右边是空白的,只有完全到达盒子宽的时候,整个图片才会显示完整,用户体验很差,所以需要用到障眼法,就是用14张图片。<style type=“text/css”> * { margin: 0; padding: 0; list-style: none; } .box { width: 882px; height: 84px; border: 1px solid #ccc; margin: 50px auto; overflow: hidden; } ul { width: 1764px; height: 84px; float: right; animation: move 10s linear infinite; } li { float: left; } ul:hover { animation-play-state: paused; } @keyframes move { from { transform: translateX(0px); } to { transform: translateX(882px); } }</style><div class=“box”> <ul> <li><img src=“img/1.jpg” alt=""></li> <li><img src=“img/2.jpg” alt=""></li> <li><img src=“img/3.jpg” alt=""></li> <li><img src=“img/4.jpg” alt=""></li> <li><img src=“img/5.jpg” alt=""></li> <li><img src=“img/6.jpg” alt=""></li> <li><img src=“img/7.jpg” alt=""></li> <li><img src=“img/1.jpg” alt=""></li> <li><img src=“img/2.jpg” alt=""></li> <li><img src=“img/3.jpg” alt=""></li> <li><img src=“img/4.jpg” alt=""></li> <li><img src=“img/5.jpg” alt=""></li> <li><img src=“img/6.jpg” alt=""></li> <li><img src=“img/7.jpg” alt=""></li> </ul></div>效果图:5、进度条前面已经接触了一个demo,只不过是一个静态的进度条,现在我们通过动画,让他动起来。<style type=“text/css”> * { margin: 0; padding: 0; } html,body { width: 100%; height: 100%; background-color: #ccc; } .line { width: 980px; height: 40px; margin: 50px auto; border-radius: 10px; background-image: linear-gradient( 135deg, #fff 25%, #000 25%, #000 50%, #fff 50%, #fff 75%, #000 75%, #000 ); background-size: 40px 40px; animation: move 2s linear infinite; } @keyframes move { from { background-position: 0px; } to { background-position: 40px; } }</style><div class=“line”></div>效果图:6、时钟案例本时钟案例,主要是依赖CSS3中动画以及旋转的概念实现的。<style type=“text/css”> * { margin: 0; padding: 0; } body{ background: #ccc; } .box { width: 300px; height: 300px; border: 1px solid #fff; margin: 100px auto; border-radius: 50%; position: relative; } .line { position: absolute; left: 50%; margin-left: -1px; height: 100%; width: 2px; background-color: #fff; } .line:nth-child(1){ } / 设置时刻位置的刻度线 / .line:nth-child(2) { transform: rotateZ(30deg); } .line:nth-child(3) { transform: rotateZ(60deg); } .line:nth-child(4) { transform: rotateZ(90deg); } .line:nth-child(5) { transform: rotateZ(120deg); } .line:nth-child(6) { transform: rotateZ(-30deg); } .cover { position: absolute; left: 50%; top: 50%; margin-left: -140px; margin-top: -140px; width: 280px; height: 280px; background-color: #fff; border-radius: 50%; } / 设置时针转完一圈所要用的时间为 43200s / .h{ width: 6px; height: 100px; background-color: #000; position: absolute; left: 50%; top: 40px; margin-left: -3px; animation: rotate 43200s linear infinite; } / 设置分针转完一圈所要用的时间为 3600s / .m { width: 4px; height: 120px; background-color: #000; position: absolute; left: 50%; top: 20px; margin-left: -2px; transform-origin: bottom; animation: rotate 3600s linear infinite; } / 设置秒针转完一圈所要用的时间为 60s / / steps(60) 表示该动画需要60步完成 / .s { width: 2px; height: 130px; background-color: orange; position: absolute; left: 50%; top: 10px; margin-left: -1px; transform-origin: bottom; animation: rotate 60s steps(60) infinite; } .dotted { width: 10px; height: 10px; background-color: #000; border-radius: 50%; position: absolute; left: 50%; top: 50%; margin-left: -5px; margin-top: -5px; } / 定义旋转角度动画,从0 度旋转到 360度 / @keyframes rotate { from { transform: rotateZ(0deg); } to { transform: rotateZ(360deg); } }</style><div class=“box”> <div class=“line”></div> <div class=“line”></div> <div class=“line”></div> <div class=“line”></div> <div class=“line”></div> <div class=“line”></div> <div class=“cover”> <div class=“h”></div> <div class=“m”></div> <div class=“s”></div> <div class=“dotted”></div> </div></div>效果图:7、魔方案例<style type=“text/css”> * { margin: 0; padding: 0; list-style: none; } .box { width: 400px; height: 400px; margin: 150px auto; / perspective: 1000px; / transform-style: preserve-3d; animation: rotate 5s linear infinite alternate; } .box>div { position: absolute; width: 100%; height: 100%; } .left { background-color: transparent; transform: rotateY(-90deg) translateZ(200px); } .right { background-color: transparent; transform: rotateY(90deg) translateZ(200px); } .top { background-color: transparent; transform: rotateX(90deg) translateZ(200px); } .bottom { background-color: transparent; transform: rotateX(-90deg) translateZ(200px); } .before { background-color: transparent; transform: translateZ(200px); } .back { transform: translateZ(-200px); background-color: transparent; } @keyframes rotate { 0% { transform: rotateY(0deg) rotateX(0deg) rotateZ(0deg); } 50% { transform: rotateY(30deg) rotateX(60deg) rotateZ(45deg); } 75% { transform: rotateY(80deg) rotateX(-60deg) rotateZ(-45deg); } 100% { transform: rotateY(-45deg) rotateX(50deg) rotateZ(-35deg); } } li { width: 120px; height: 120px; background-color: green; float: left; margin-left: 10px; margin-top: 10px; text-align: center; line-height: 120px; font-size: 50px; border-radius: 10px; } .left li { background-color: orange; } .right li { background-color: pink; } .top li { background-color: red; } .bottom li { background-color: yellow; } .back li { background-color: orangered; }</style><div class=“box”> <div class=“left”> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> </div> <div class=“right”> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> </div> <div class=“top”> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> </div> <div class=“bottom”> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> </div> <div class=“before”> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> </div> <div class=“back”> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> </div></div>效果图:12. 伸缩布局本章节摘自阮一峰老师的《Flex 布局教程》CSS3在布局方面做了非常大的改进,使得我们对块级元素的布局排列变得十分灵活,适应性非常强,其强大的伸缩性,在响应式开中可以发挥极大的作用。伸缩布局也叫弹性布局或者flex布局。12.1 什么是伸缩布局Flex是Flexible Box的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为Flex布局。.box{ display: flex;}行内元素也可以使用Flex布局。.box{ display: inline-flex;}Webkit内核的浏览器,必须加上-webkit前缀。.box{ display: -webkit-flex; / Safari / display: flex;}注意,设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。12.2 基本概念浏览器兼容性:学习几个新概念:主轴:Flex容器的主轴主要用来配置Flex项目,默认是水平方向侧轴:与主轴垂直的轴称作侧轴,默认是垂直方向的方向:默认主轴从左向右,侧轴默认从上到下主轴和侧轴并不是固定不变的,通过flex-direction可以互换。12.3 flex-direction属性flex-direction属性决定主轴的方向(即项目的排列方向)。.box { flex-direction: row | row-reverse | column | column-reverse; }参数说明:row(默认值):主轴为水平方向,起点在左端。row-reverse:主轴为水平方向,起点在右端。column:主轴为垂直方向,起点在上沿。column-reverse:主轴为垂直方向,起点在下沿。示例代码:<style> .block{ width: 100%; border-radius: 15px; background: rgba(158,158,158,0.15); padding: 20px; box-sizing: border-box; margin-bottom: 20px; } .box{ background: #fff; display: flex; margin: 0 0 55px; } .box-item { width: 150px; height: 150px; line-height: 150px; vertical-align: middle; margin: 5px; background-color: #ffd200; font-size: 100px; color: white; text-align: center; } .box-1-1 { flex-direction: row; } .box-1-2 { flex-direction: row-reverse; } .box-1-3 { flex-direction: column; } .box-1-4 { flex-direction: column-reverse; }</style><div class=“block”> <h4>flex-direction: row 水平向右</h4> <div class=“box box-1-1”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> </div></div><div class=“block”> <h4>flex-direction: row-reverse 水平向左</h4> <div class=“box box-1-2”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> </div></div><div class=“block”> <h4>flex-direction: column 垂直向下</h4> <div class=“box box-1-3”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> </div></div><div class=“block”> <h4>flex-direction: column-reverse 垂直向上</h4> <div class=“box box-1-4”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> </div></div>效果图:12.4 flex-wrap 属性默认情况下,项目都排在一条线(又称"轴线")上。flex-wrap属性定义,如果一条轴线排不下,如何换行。.box{ flex-wrap: nowrap | wrap | wrap-reverse; }参数说明:nowrap(默认):不换行,宽度自动适应;wrap:换行,第一行在上方;wrap-reverse:换行,第一行在下方示例代码:<style> .block{ width: 100%; border-radius: 15px; background: rgba(158,158,158,0.15); padding: 20px; box-sizing: border-box; margin-bottom: 20px; } .box{ background: #fff; display: flex; margin: 0 0 55px; } .box-item { width: 150px; height: 150px; line-height: 150px; vertical-align: middle; margin: 5px; background-color: #ffd200; font-size: 100px; color: white; text-align: center; } .box-2-1 { flex-wrap: nowrap; } .box-2-2 { flex-wrap: wrap; } .box-2-3 { flex-wrap: wrap-reverse; } </style><div class=“block”> <h4>nowrap(默认):不换行。</h4> <div class=“box box-2-1”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> <div class=“box-item”>5</div> <div class=“box-item”>6</div> <div class=“box-item”>7</div> </div></div><div class=“block”> <h4>wrap:换行,第一行在上方。</h4> <div class=“box box-2-2”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> <div class=“box-item”>5</div> <div class=“box-item”>6</div> <div class=“box-item”>7</div> </div></div><div class=“block”> <h4>wrap-reverse:换行,第一行在下方。</h4> <div class=“box box-2-3”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> <div class=“box-item”>5</div> <div class=“box-item”>6</div> <div class=“box-item”>7</div> </div></div>效果图:12.5 flex-flow 属性flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。.box { flex-flow: <flex-direction> || <flex-wrap>;}示例代码:<style> .block{ width: 100%; border-radius: 15px; background: rgba(158,158,158,0.15); padding: 20px; box-sizing: border-box; margin-bottom: 20px; } .box{ background: #fff; display: flex; margin: 0 0 55px; } .box-item { width: 150px; height: 150px; line-height: 150px; vertical-align: middle; margin: 5px; background-color: #ffd200; font-size: 100px; color: white; text-align: center; } .box-3{ flex-flow: row wrap; }</style><div class=“block”> <h4>row nowrap(默认):不换行。</h4> <div class=“box box-3”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> <div class=“box-item”>5</div> <div class=“box-item”>6</div> <div class=“box-item”>7</div> </div></div>效果图:12.6 justify-content 属性justify-content属性定义了项目在主轴上的对齐方式。.box { justify-content: flex-start | flex-end | center | space-between | space-around;}参数说明:flex-start:(默认值):左对齐flex-end:右对齐center: 居中space-between:两端对齐,项目之间的间隔都相等。space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。示例代码:<style> .block{ width: 100%; border-radius: 15px; background: rgba(158,158,158,0.15); padding: 20px; box-sizing: border-box; margin-bottom: 20px; } .box{ background: #fff; display: flex; margin: 0 0 55px; } .box-item { width: 150px; height: 150px; line-height: 150px; vertical-align: middle; margin: 5px; background-color: #ffd200; font-size: 100px; color: white; text-align: center; } .box-4-1{ justify-content:flex-start; } .box-4-2{ justify-content:flex-end; } .box-4-3{ justify-content:center; } .box-4-4{ justify-content:space-between; } .box-4-5{ justify-content:space-around; }</style><div class=“block”> <h4>flex-start(默认值):左对齐</h4> <div class=“box box-4-1”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> </div></div><div class=“block”> <h4>flex-end:右对齐</h4> <div class=“box box-4-2”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> </div></div><div class=“block”> <h4>center: 居中</h4> <div class=“box box-4-3”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> </div></div><div class=“block”> <h4>space-between:两端对齐,项目之间的间隔都相等。</h4> <div class=“box box-4-4”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> </div></div><div class=“block”> <h4>space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。</h4> <div class=“box box-4-5”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> </div></div>效果图:12.7 align-items 属性align-items属性定义项目在交叉轴上如何对齐。.box { align-items: flex-start | flex-end | center | baseline | stretch;}参数说明:flex-start:交叉轴的起点对齐。flex-end:交叉轴的终点对齐。center:交叉轴的中点对齐。baseline: 项目的第一行文字的基线对齐。stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。示例代码:<style> .block{ width: 100%; border-radius: 15px; background: rgba(158,158,158,0.15); padding: 20px; box-sizing: border-box; margin-bottom: 20px; } .box{ background: #fff; display: flex; margin: 0 0 55px; } .box-item { width: 150px; height: 150px; line-height: 150px; vertical-align: middle; margin: 5px; background-color: #ffd200; font-size: 100px; color: white; text-align: center; } .item-tall { height: 400px; line-height: 400px; } .box-5-1 { align-items: flex-start; } .box-5-2 { align-items: flex-end; } .box-5-3 { align-items: center; } .box-5-4{ align-items: baseline; } .box-5-4 .box-item{ font-size: 88px; line-height: initial; text-decoration: underline; } .box-5-4 .item-tall{ font-size: 122px; line-height: initial; } .box-5-5 { align-items: stretch; } .box-5-5 .box-item { height: auto;}</style><div class=“block”> <h4>flex-start:交叉轴的起点对齐。</h4> <div class=“box box-5-1”> <div class=“box-item”>1</div> <div class=“box-item item-tall”>2</div> <div class=“box-item”>3</div> <div class=“box-item item-tall”>4</div> </div></div><div class=“block”> <h4>flex-end:交叉轴的终点对齐。</h4> <div class=“box box-5-2”> <div class=“box-item”>1</div> <div class=“box-item item-tall”>2</div> <div class=“box-item”>3</div> <div class=“box-item item-tall”>4</div> </div></div><div class=“block”> <h4>center:交叉轴的中点对齐。</h4> <div class=“box box-5-3”> <div class=“box-item”>1</div> <div class=“box-item item-tall”>2</div> <div class=“box-item”>3</div> <div class=“box-item item-tall”>4</div> </div></div><div class=“block”> <h4>baseline: 项目的第一行文字的基线对齐。</h4> <div class=“box box-5-4 line”> <div class=“box-item”>1</div> <div class=“box-item item-tall”>2</div> <div class=“box-item”>3</div> <div class=“box-item item-tall”>4</div> </div></div><div class=“block”> <h4>stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。</h4> <div class=“box box-5-5”> <div class=“box-item”>1</div> <div class=“box-item item-tall”>2</div> <div class=“box-item”>3</div> <div class=“box-item item-tall”>4</div> </div></div>效果图:12.8 align-content 属性align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。.box { align-content: flex-start | flex-end | center | space-between | space-around | stretch;}参数说明:flex-start:与交叉轴的起点对齐。flex-end:与交叉轴的终点对齐。center:与交叉轴的中点对齐。space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。stretch(默认值):轴线占满整个交叉轴。示例代码:<style> .box-tall { height: 800px; } .box-6-1 { flex-wrap: wrap; align-content: flex-start; } .box-6-2 { flex-wrap: wrap; align-content: flex-end; } .box-6-3 { flex-wrap: wrap; align-content: center; } .box-6-4 { flex-wrap: wrap; align-content: space-between } .box-6-5 { flex-wrap: wrap; align-content: space-around; } .box-6-6 { flex-wrap: wrap; align-content: stretch; } .box-6-6 .box-item { height: auto; }</style><div class=“block”> <h4>flex-start:交叉轴的起点对齐。</h4> <div class=“box box-tall box-6-1”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> <div class=“box-item”>5</div> <div class=“box-item”>6</div> <div class=“box-item”>7</div> <div class=“box-item”>8</div> <div class=“box-item”>9</div> <div class=“box-item”>10</div> <div class=“box-item”>11</div> <div class=“box-item”>12</div> </div></div><div class=“block”> <h4>flex-end:与交叉轴的终点对齐。</h4> <div class=“box box-tall box-6-2”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> <div class=“box-item”>5</div> <div class=“box-item”>6</div> <div class=“box-item”>7</div> <div class=“box-item”>8</div> <div class=“box-item”>9</div> <div class=“box-item”>10</div> <div class=“box-item”>11</div> <div class=“box-item”>12</div> </div></div><div class=“block”> <h4>center:与交叉轴的中点对齐。</h4> <div class=“box box-tall box-6-3”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> <div class=“box-item”>5</div> <div class=“box-item”>6</div> <div class=“box-item”>7</div> <div class=“box-item”>8</div> <div class=“box-item”>9</div> <div class=“box-item”>10</div> <div class=“box-item”>11</div> <div class=“box-item”>12</div> </div></div><div class=“block”> <h4>space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。</h4> <div class=“box box-tall box-6-4”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> <div class=“box-item”>5</div> <div class=“box-item”>6</div> <div class=“box-item”>7</div> <div class=“box-item”>8</div> <div class=“box-item”>9</div> <div class=“box-item”>10</div> <div class=“box-item”>11</div> <div class=“box-item”>12</div> </div></div><div class=“block”> <h4>space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。</h4> <div class=“box box-tall box-6-5”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> <div class=“box-item”>5</div> <div class=“box-item”>6</div> <div class=“box-item”>7</div> <div class=“box-item”>8</div> <div class=“box-item”>9</div> <div class=“box-item”>10</div> <div class=“box-item”>11</div> <div class=“box-item”>12</div> </div></div><div class=“block”> <h4>stretch(默认值):轴线占满整个交叉轴。</h4> <div class=“box box-tall box-6-6”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item”>4</div> <div class=“box-item”>5</div> <div class=“box-item”>6</div> <div class=“box-item”>7</div> <div class=“box-item”>8</div> <div class=“box-item”>9</div> <div class=“box-item”>10</div> <div class=“box-item”>11</div> <div class=“box-item”>12</div> </div></div>效果图:12.9 Order 属性order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。.item { order: <integer>; }参数说明:order属性值是一个数值,可以是正数,也可以是负数,在当前项目里,数值越小,排列越靠前。示例代码:<style> .box-7 .order { order: -1; } .box-7 .box-item div{ font-size: 14px; position: relative; bottom: 90px; color: #9236f4; }</style><div class=“box box-7”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> <div class=“box-item order”>4 <div>(order:-1)</div></div></div>效果图:12.10 flex-grow 属性flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。.item { flex-grow: <number>; / default 0 / }参数说明:如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。示例代码:<style> .box-8 .box-item div{ font-size: 14px; position: relative; bottom: 90px; color: #9236f4; } .box-8 .grow{ flex-grow: 1; width: auto; } .box-8 .grow-2 { flex-grow: 2; }</style><div class=“block”> <div class=“box box-8”> <div class=“box-item grow”>1 <div>flex-grow: 1</div></div> <div class=“box-item grow grow-2”>2 <div>flex-grow: 2</div></div> <div class=“box-item grow”>3 <div>flex-grow: 1</div></div> </div></div>效果图:12.11 flex-shrink 属性flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。.item { flex-shrink: <number>; / default 1 / }参数说明:如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。负值对该属性无效。示例代码:<style> .box-9 .box-item div{ font-size: 14px; position: relative; bottom: 90px; color: #9236f4; } .box-9 .box-item { width: 400px; } .box-9 .shrink{ flex-shrink: 0; }</style><div class=“block”> <div class=“box box-9”> <div class=“box-item shrink”>1 <div>flex-shrink: 0</div></div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> </div></div>效果图:12.12 flex-basis 属性flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main-size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。.item { flex-basis: <length>; | auto; / default auto / }示例代码:<style> .box-10 .box-item { flex-basis: 200px; width: 400px; / width 将失去作用*/ }</style><div class=“block”> <h4></h4> <div class=“box box-10”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item”>3</div> </div></div>效果图:12.13 flex 属性flex属性是flex-grow, flex-shrink和flex-basis的简写,默认值为0 1 auto。后两个属性可选。该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。.item { flex: none | [ <‘flex-grow’> <‘flex-shrink’>? || <‘flex-basis’> ] }12.14 align-self 属性align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。.item { align-self: auto | flex-start | flex-end | center | baseline | stretch;}参数说明:auto:表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。flex-start:交叉轴的起点对齐。flex-end:交叉轴的终点对齐。center:交叉轴的中点对齐。baseline: 项目的第一行文字的基线对齐。stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。示例代码:<style> .box-11 { height: 400px; } .box-11 .box-item { align-self: flex-start; } .box-11 .box-item div{ font-size: 14px; position: relative; bottom: 90px; color: #9236f4; } .box-11 .end { align-self: flex-end; }</style><div class=“block”> <h4>stretch(默认值):轴线占满整个交叉轴。</h4> <div class=“box box-11”> <div class=“box-item”>1</div> <div class=“box-item”>2</div> <div class=“box-item end”>3<div>flex-end</div></div> <div class=“box-item”>4</div> </div></div>效果图:

November 26, 2018 · 14 min · jiezi

[盘点]项目中可以怎么优化图片

看似平常的事物,往往会蕴含的巨大的智慧。把看似平常的事物简单做好,可能很正常。如果能把平常的事物做精,做细,这个不平常。1.前言每一个开发者在开发项目中,不可避免要和图片打交道,优化图片似乎也成了一个必修课。图片优化也不仅仅是性能上的优化,还要进行体验上的优化。至于怎么优化图片,没有固定的方式,只能具体场景,具体分析,选择合适的方案。不多说,下面也简单介绍下自己处理过,了解过的一些方式。如果大家有补充,建议。欢迎在评论区留言,交流学习下。2.概念用法‘概念用法’这个词是自己乱起的,可能不太准确,是因为词穷了,不知道怎样形容。总得来说,这部分介绍的处理方式,就是讲一下就知道怎么用的方式,不需要怎么放代码,运行图等。只需要笼统的介绍一下,大家都会懂的一些方式。2-1.图片压缩这个没有隐含的意思,就是把图片的大小进行压缩。目前自己用的比较多的两个压缩网站是TinyPng和智图。使用比较方便,品质也基本保持一致。2-2.base64代替小图标一些比较小的图标,使用 base64 编码代替可以减少 http 请求。但是有一个缺点就是转成 base64 后,编码会比原图更大,图片越大,差别就越大。1K左右的图标,转码出来的 base64 大概是 1.1K-2K。如果是 8K 的图片,转码出来的 base64 可能超过10K。就自己项目开发而言,只有小于 4K 的图标,才会进行转码。2-3.icon-font代替图标由于 icon-font 看着是图片,实际上是字体。优点:就是在于可以矢量缩放,大小图标都可以使用,也可以改变颜色,使用也不麻烦。缺点:需要引入的文件不少(.svg,.ttf,.woff,.eot )。文件大小也比较大。建议是项目的图标要达到一定量才使用 icon-font,如果是几个图标,还是用图片吧。如果需要引入的图标多,就建议使用 icon-font。上面说的 icon-font 由于是字体,所以不支持多色图标。有了解到,现在 icon-font 可以支持多色图标了(symbol引用)。只是兼容性不好。2-4.雪碧图雪碧图就是把很多小的图整合到一起,制作成一张比较大的图,然后作为元素的背景图片使用,定位到相应的图片即可。优点:减少了大量的 http 请求。缺点:背景定位和在移动端适配大小有点麻烦。除此之外,使用雪碧图,有两个个注意地方1.不要把页面所有的图片都合并,比如把 logo 整合会破坏 html 的语义结构。图像复杂的 banner 也不要合并2.尽量只把颜色相近的图标整合在一张图片上,如果图片颜色相差太大,合并出来的图片可能会很大。2-5.响应式图片比如页面上有一张尺寸是 100100 的图片,但是图片的实际尺寸是 10001000 的。这样的情况建议在多准备一张 100*100 的图片。不然可能会造成资源浪费。2-6.混合模式代替变色的图标如下例子,比如页面有这个图标在特定情况下会是下面这个颜色。同一个图标,在不同的时候是不同的颜色。icon-font 可以通过改变 color 实现。或者用两张图片。除了这两个方法,用 CSS3 的混合模式,一样可以实现。两行代码搞定。<!DOCTYPE html><html> <head> <meta charset=“UTF-8”> <title></title> <style> div{ /容器必须有背景/ background: #09f; display: inline-block; } img{ width: 100px; vertical-align: top; } img:hover{ /设置混合模式/ mix-blend-mode: lighten; } </style> </head> <body> <div><img src=“images/icon-good.jpg” class=“u-icon”/></div> <div><img src=“images/icon-good.png” class=“u-icon”/></div> </body></html>运行效果展示完 mix-blend-mode,顺便提下 background-blend-mode 。用法基本一致,只是 mix-blend-mode 作用于 html 元素的混合模式,background-blend-mode 作用于元素背景的混合模式。<!DOCTYPE html><html> <head> <meta charset=“UTF-8”> <title></title> <style> div{ display: inline-block; width: 100px; height: 100px; /设置背景/ background: url(images/icon-good.jpg) no-repeat center,#09f; background-size:100%; /设置背景混合模式/ background-blend-mode: lighten; } </style> </head> <body> <div></div> </body></html>注意事项1.图片必须是白底纯色图标2.现代的浏览器,支持这个属性的浏览器如果图片是透明纯色背景,得到的结果会是这样受限篇幅影响,混合模式暂时就介绍这么多,以后发现好玩的再写文章。有兴趣可以看下面的参考资料。两行 CSS 代码实现图片任意颜色赋色技术不可思议的颜色混合模式 mix-blend-mode不可思议的混合模式 background-blend-mode2-7.简单图标使用 CSS 画有一些简单的图标,可以使用 CSS 代替。比如下面这些自己而言,项目上画的最多的就是各种箭头<!DOCTYPE html><html> <head> <meta charset=“UTF-8”> <title></title> <style> .icon-arrow-bottom { width: 0; height: 0; border: 100px solid #000; border-color: #000 transparent transparent transparent; } .icon-arrow-top { width: 0; height: 0; border: 100px solid #000; border-color: transparent transparent #000 transparent; } </style> </head> <body> <div class=“icon-arrow-bottom”></div> <div class=“icon-arrow-top”</div> </body></html>优点:矢量缩放,颜色可变,不需要发送请求缺点:只适合用简单图形,1-5行 CSS 代码可以搞定的才建议用,超过的不建议。想得痛苦,写也麻烦,花时间也多,效果未必比其它方案好。建议还是图片 base64,或者 icon-font。这里就简单举个例子,需要知道 css3 还可以画什么图形。看参考资料。纯CSS制作的图形效果奇妙的 CSS shapes(CSS图形)【CSS】用CSS绘制图标(图标大全)3.隐式预加载1.从这里开始。下面的demo,有些会用到 ecDo 这个库(自己写的一个常用函数库,欢迎star)。之前的文章有介绍过,这里就不再重复。大家不知道的时候点开看下相应的 API ,运行下,调试下就好。2.为方便展示,下面的demo,除了懒加载,都有在 network 把网速调至了慢速的3G。有些项目图片比较多,如果一次性加载,用户等待时间会过久,可能会造成体验效果很差,甚至导致用户流失,很多网站用到的一个体验优化方式是隐式预加载。等待首屏加载,在用户看首屏(第一张大图)的时候,悄悄的加载其它图片(这里为了展示效果,在项目上其他的小图片不应在第一屏)。<body> <p><img src=“lawyerOtherImg.jpg”/></p> <p>这是预加载的图片</p> <div> <img data-src=“https://materialdb.zhaoyl.com/201809/106796.jpg" class=“load-img” width=“100” height=“100”/><img data-src=“https://materialdb.zhaoyl.com/201809/105567.jpg" class=“load-img” width=“100” height=“100”/><img data-src=“https://materialdb.zhaoyl.com/201809/103097.jpg" class=“load-img” width=“100” height=“100”/><img data-src=“https://materialdb.zhaoyl.com/201809/10205.jpg" class=“load-img” width=“100” height=“100”/><img data-src=“https://materialdb.zhaoyl.com/201809/001.jpg" class=“load-img” width=“100” height=“100”/> </div></body>//测试请先清空缓存window.onload = function () { ecDo.loadImg(’load-img’, function () { console.log(‘加载完毕’) });}注意事项:1.大概预测,用户看首屏的时候,很大概率会往下面看。2.该方式,用户等待的时间比较短。但是图片超大,要慎重考虑。因为该方式无法保证用户在浏览的时候,能把下一屏(比如浏览第一屏的时候,要加载第二屏)的图片加载完毕,让用户无感知。如果切换的下一屏还没加载完毕,也可能会影响体验。demo:https://github.com/chenhuiYj/…4.显式预加载告诉用户正在加载,等到加载完了再一次性渲染在页面上。<style> div{ display: none; }</style><body> <p id=“p”>显示预加载进行中</p> <div id=“div”> <img data-src=“https://materialdb.zhaoyl.com/201809/106796.jpg" class=“load-img” width=“100” height=“100”/><img data-src=“https://materialdb.zhaoyl.com/201809/105567.jpg" class=“load-img” width=“100” height=“100”/><img data-src=“https://materialdb.zhaoyl.com/201809/103097.jpg" class=“load-img” width=“100” height=“100”/><img data-src=“https://materialdb.zhaoyl.com/201809/10205.jpg" class=“load-img” width=“100” height=“100”/><img data-src=“https://materialdb.zhaoyl.com/201809/001.jpg" class=“load-img” width=“100” height=“100”/> </div></body>let oP1=document.getElementById(‘p’);let oDiv=document.getElementById(‘div’);//测试请先清空缓存window.onload = function () { ecDo.loadImg(’load-img’, function () { oDiv.style.display=‘block’; oP1.style.display=‘none’; });}注意事项:1.大概预测,用户看首屏的时候,很大概率会往下面看。2.该方式好处在于加载完毕之后,就所有图片都加载完毕了,体验比较好。如果图片全部过大,加载时间会比较长,loading 的时间也会很长,会影响体验。demo地址:https://github.com/chenhuiYj/…5.懒加载这个大家应该很熟悉了,简单点说就是图片一开始不加载,当用户浏览到什么位置的时候,相应位置得图片就加载出来。<body> <p><img data-src=“lawyerOtherImg.jpg” class=“load-img” width=‘528’ height=‘304’/></p> <p><img data-src=“lawyerOtherImg.jpg” class=“load-img” width=‘528’ height=‘304’/></p> <p><img data-src=“lawyerOtherImg.jpg” class=“load-img” width=‘528’ height=‘304’/></p> <p><img data-src=“https://materialdb.zhaoyl.com/201809/105567.jpg" class=“load-img” width=‘528’ height=‘304’/></p> <p><img data-src=“https://materialdb.zhaoyl.com/201809/106796.jpg" class=“load-img” width=‘528’ height=‘304’/></p> <p><img data-src=“https://materialdb.zhaoyl.com/201809/103097.jpg" class=“load-img” width=‘528’ height=‘304’/></p> <p><img data-src=“https://materialdb.zhaoyl.com/201809/10205.jpg" class=“load-img” width=‘528’ height=‘304’/></p> <p><img data-src=“https://materialdb.zhaoyl.com/201809/001.jpg" class=“load-img” width=‘528’ height=‘304’/></p></body>window.onload = function () { //根据load-img 这个 class 遍历,元素距离页面底部 100像素的时候就开始加载,加载错误就显示error.jpg ecDo.delayFn(ecDo.lazyLoadImg(’load-img’, 100, ’error.jpg’),100,200); window.onscroll = function () { ecDo.delayFn(ecDo.lazyLoadImg(’load-img’, 100, ’error.jpg’),100,200); }}demo:https://github.com/chenhuiYj/…6.图片没加载出来显示默认图片这个例子,当网速比较慢的时候,想要加载的图片没有马上出来。或者图片路径错误,这个时候页面可能会出现一部分空白的地方,或者页面布局会出现错乱,比较常用的做法是先显示一张 loading 图或者是 logo 图。告诉用户,这里是图片,正在加载,体验上会好很多,比如下面这个例子。下面也简单的实现一下。比如网站上有这样的图片<p><img src=“error.jpg” data-src=“https://materialdb.zhaoyl.com/201809/105567.jpg" width=“264”/></p><p><img src=“error.jpg” data-src=“https://materialdb.zhaoyl.com/201809/106796.jpg" width=‘264’/></p><p><img src=“error.jpg” data-src=“https://materialdb.zhaoyl.com/201809/1067961.jpg" width=‘264’/></p>在 network 把网速调至了慢速的3G,以方便调试。//测试前请先清空缓存window.onload = function () { let oImg=document.getElementsByTagName(‘img’); for(let i=0;i<oImg.length;i++){ ecDo.aftLoadImg({ dom:oImg[i], url:oImg[i].dataset.src, errorUrl:oImg[i].src }) }}可以看到,一开始显示的是一张默认图片,等需要加载的图片,加载完了之后,再加载需要加载的图片。(最后一张图片,是故意把路径写错,所以出来的图片是之前的图片)demo:https://github.com/chenhuiYj/…7.小结关于项目上,优化图片的各种方式,自己用过的,听过的,大概就在这里了。实现方案,也不敢说是最好。如果大家有更好的想法,建议,欢迎在评论区留言。————————-华丽的分割线——————–想了解更多,和我交流,内推职位,请添加我微信。或者关注我的微信公众号:乐趣区 ...

November 26, 2018 · 2 min · jiezi

CSS3 入门详解(一)

前言如同人类的的进化一样,CSS3是CSS2的“进化”版本,在CSS2基础上,增强或新增了许多特性,弥补了CSS2的众多不足之处,使得Web开发变得更为高效和便捷。1.选择器CSS3新增了许多灵活查找元素的方法,极大的提高了查找元素的效率和精准度。CSS3选择器与jQuery中所提供的绝大部分选择器兼容。1.1 属性选择器属性选择器就是通过属性来选择元素。选择器含义[attr]存在attr属性即可[attr=val]属性值完全等于val[attr*=val]属性值里包含val字符并且在“任意”位置[attr^=val]属性值里包含val字符并且在“开始”位置[attr$=val]属性值里包含val字符并且在“结束”位置1、[attr]<style> /* 所有拥有class属性的标签,添加color样式 / [class]{ color: #333; }</style>2、[attr=val]<style> / 所有拥有class属性全等于“one”的标签,添加color样式 / [class = “one”]{ color: #333; }</style>3、[attr=val]<style> /* class属性的值里面包含“one”的标签,添加color样式 / [attr=“one”]{ color: #333; }</style>4、[attr^=val]<style> /* class属性的值以“one”开始的标签,添加color样式 / [attr ^= “one”]{ color: #333; }</style>5、[attr$=val]<style> / class属性的值以“one”结束的标签,添加color样式 / [attr $= “one”]{ color: #333; }</style>1.2 伪类选择器除了以前介绍的的:link、:active、:visited、:hover,CSS3又新增了其它的伪类选择器。1、结构(位置)伪类选择器含义:first-child其父元素的第1个子元素:last-child其父元素的最后1个子元素:nth-child(n)其父元素的第n个子元素:nth-last-child(n)其父元素的第n个子元素(倒着数)2、空伪类:empty选中没有任何子节点的元素<style> div:empty { / 没有子元素的div元素 / width: 100px; height: 100px; background-color: pink; }</style><!– css 样式不起作用 –><div class=“one”>阿斯蒂芬</div> <!– css样式不起作用 –><div> <p></p></div><!– css样式生效 –><div></div>3、目标伪类:target结合锚点进行使用,处于当前锚点的元素会被选中;<style type=“text/css”> / 使用锚链接指向当前标签的时候 / .one:target { background-color: pink; font-size: 30px; }</style><a href="#hh">找笑脸去</a><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p id=“hh” class=“one”>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p><p>阿斯顿发撒旦法撒打发放大法的撒双方都</p>4、排除伪类:not(selector)除selector(任意选择器)外的元素会被选中;<style> / 除了类名是“.not”的div元素 / div:not(.one) { width: 100px; height: 100px; background-color: pink; }</style><!– css样式不生效 –><div class=“one”></div><!– css样式生效 –><p></p><!– css样式生效 –><div></div>1.3 伪元素选择器1、::first-letter文本的第一个单词或字(如中文、日文、韩文等)2、::first-line 文本第一行;3、::selection 可改变选中文本的样式;4、::before和::after示例代码:伪元素实现横竖分割线<style type=“text/css”> * { margin: 0; padding: 0; list-style: none; } .box { width: 300px; height: 200px; background-color: pink; margin: 50px auto; } .box li { width: 100px; height: 100px; float: left; background-color: #555; position: relative; overflow: hidden; } li:before { content: “”; display: block; width: 90px; height: 1px; background-color: #ccc; position: absolute; top: 97px; left: 5px; } li:after { content: “”; display: block; width: 1px; height: 90px; background-color: #ccc; position: absolute; left: 0px; top: 4px; } li:nth-child(1):after,li:nth-child(4):after { display: none; } li:nth-last-child(-n+3):before { display: none; }</style><div class=“box”> <ul> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul></div>效果图::after、:before在旧版本里是伪元素,CSS3的规范里“:”用来表示伪类,“::”用来表示伪元素,但是在高版本浏览器下:after、:before会被自动识别为::after、::before,这样做的目的是用来做兼容处理。2. 颜色新增了RGBA、HSLA模式,其中的A表示透明度,即可以设置颜色值的透明度,相较opacity,它们不具有继承性,即不会影响子元素的透明度。2.1 RGBARed、Green、Blue、Alpha即RGBA,R、G、B取值范围0255。<style> #box{ width:100px; height:100px; background: rgba(200,200,200,.5); }</style><div id=“box”></div>2.2 HSLAH 色调 取值范围0360,0/360表示红色、120表示绿色、240表示蓝色S 饱和度 取值范围0%100%L 亮度 取值范围0%100%A 透明度 取值范围01<style> #box{ width:100px; height:100px; background: hsla(200,50%,50%,.5); }</style><div id=“box”></div>2.3 关于 CSS 的透明度Alpha和opacity的区别主要就是,opacity具有继承性,父盒子设置该属性,下面所有的子元素都会继承该属性。transparent不可调节透明度,始终完全透明。3. 文本阴影text-shadow,可分别设置偏移量、模糊度、颜色(可设透明度)。如:text-shadow: 2px 2px 2px #CCC;1、水平偏移量 正值向右 负值向左;2、垂直偏移量 正值向下 负值向上;3、模糊度是不能为负值;4、阴影颜色。示例代码:文字浮雕<style> html,body { margin: 0; padding: 0; width: 100%; height: 100%; background-color: #999; font-size: 50px; text-align: center; line-height: 260px; color: #999; } .one { text-shadow: -1px -1px 1px #fff,1px 1px 1px #000; } .two { text-shadow: -1px -1px 1px #000,1px 1px 1px #fff; }</style><div class=“one”>我是凸起文字</div><div class=“two”>我是凹下去的文字</div>效果图:4. 盒模型CSS3中可以通过box-sizing来指定盒模型,即可指定为content-box、border-box,这样我们计算盒子大小的方式就发生了改变。可以分成两种情况:1、box-sizing: border-box盒子大小为width2、box-sizing: content-box盒子大小为width + padding + border注:上面的标注的width指的是CSS属性里设置的width: length,content的值是会自动调整的。示例代码:<style type=“text/css”> .box { width: 316px; height: 170px; float: left; margin-left: 20px; box-sizing: border-box; } .box img { width: 100%; height: 100%; } .box:hover { border: 10px solid #00eeff; }</style><div class=“box”> <img src=“1.jpg” alt=""></div><div class=“box”> <img src=“1.jpg” alt=""></div>效果图:可以看出通过设置盒子模型后,虽然.box设置了边框,但是整个box的盒子大小没有改变。5. 边框边框中的边框圆角、边框阴影属性,应用十分广泛,兼容性也相对较好,具有符合渐进增强原则的特征。5.1 边框圆角通过border-radius属性,设置边框圆角,圆角处理时,脑中要形成圆、圆心、横轴、纵轴的概念,正圆是椭圆的一种特殊情况。为了方便表述,我们将四个角标记成1、2、3、4,如2代表右上角,CSS里提供了border-radius来设置这些角横纵轴半径值。分别设置横纵轴半径,以“/”进行分隔,遵循“1,2,3,4”规则,“/”前面的14个用来设置横轴半径(分别对应横轴1、2、3、4位置),“/”后面1~4个参数用来设置纵轴半径(分别对应纵轴1、2、3、4位置 )。<style type=“text/css”> .box { margin: 50px auto; width: 300px; height: 500px; border: 1px solid #ccc; border-radius: 10px 20px 50px 70px / 10px 20px 50px 70px; }</style><div class=“box”></div>效果图:一般情况下,我们用不到这么复杂的,除非特殊需求的时候。支持简写模式,具体如下:1、border-radius: 10px;表示四个角的横纵轴半径都为10px;2、border-radius: 10px 5px;表示1和3角横纵轴半径都为10px,2和4角横纵轴半径为5px;3、border-radius: 10px 5px 8px;表示1角模纵轴半径都为10px,2和4角横纵轴半径都为8px,3角的横纵轴半径都为8px;4、border-radius: 10px 8px 6px 4px;表示1角横纵轴半径都为10px,表示2角横纵轴半径都为8px,表示3角横纵轴半径都为6px,表示4角横纵轴半径都为6px;椭圆的画法:<style type=“text/css”> .box { margin: 50px auto; width: 300px; height: 500px; border: 1px solid #ccc; / 当盒子长宽不一致时,圆角属性 分别设置宽度的一半,以及长度的一半,即是椭圆 / / 或者直接 border-radius:50%; / border-radius: 150px 250px; }</style><div class=“box”></div>如果不想计算,直接设百分比:“50%”。正圆的画法:<style type=“text/css”> .box { margin: 50px auto; width: 200px; height: 200px; border: 1px solid #ccc; / 当盒子长宽相等时,圆角属性分别设置宽度的一半,以及长度的一半,即是正圆 / / 或者直接 border-radius:50%; / border-radius: 100px; }</style><div class=“box”></div>示例代码:边框圆角合集<style> * { margin: 0; padding: 0; list-style: none; background-color: wheat; overflow: hidden; } .box { width: 980px; height: 400px; background-color: #fff; margin: 50px auto; } .box li { float: left; width: 193px; height: 193px; background-color: #fff; margin:5px; box-shadow: 2px 3px 5px #aaa; } li:first-child:after { content: “”; height: 130px; width: 130px; margin: 30px auto; display: block; border: 1px solid orangered; border-radius: 50%; } li:nth-child(2):after { content: “”; display: block; height: 130px; width: 130px; border: 1px solid orangered; margin: 30px auto; border-radius: 65px 65px 0px 0px; } li:nth-child(3):after { content: “”; display: block; width: 130px; height: 65px; border: 1px solid orangered; margin: 50px auto; border-radius: 65px 65px 0px 0px; } li:nth-child(4):after { content: “”; display: block; width: 130px; height: 130px; border: 1px solid orangered; margin: 20px auto; border-radius: 65px 0px 0px 0px; } li:nth-child(5):after { content: “”; width: 130px; height: 65px; display: block; border: 1px solid orangered; border-radius: 50%; margin: 50px auto; } li:nth-child(6):after{ content: “”; height: 130px; width: 65px; display: block; border: 1px solid orangered; border-radius: 50%; margin: 20px auto; } li:nth-child(7):after { content: “”; height: 130px; width: 130px; display: block; border: 1px solid orangered; margin: 20px auto; border-radius: 135px 0px 0px 0px; } li:nth-child(8):after { content: “”; width: 135px; height: 30px; display: block; margin: 30px auto; border: 1px solid orangered; border-radius: 65px 65px 0px 0px / 30px 30px 0px 0px; }</style><div class=“box”> <ul> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul></div>效果图:5.2 边框阴影box-shadow,与文字阴影类似,可分别设置盒子阴影偏移量、模糊度、颜色(可设透明度)。如:box-shadow: 5px 5px 5px #CCC1、水平偏移量 正值向右 负值向左;2、垂直偏移量 正值向下 负值向上;3、模糊度是不能为负值;4、inset可以设置内阴影;注:设置边框阴影不会改变盒子的大小,即不会影响其兄弟元素的布局。可以设置多重边框阴影,实现更好的效果,增强立体感,符合渐进增强,实际开发中可以大胆使用。示例代码:<style> .box { width: 200px; height: 200px; margin: 50px auto; border: 1px dashed #000; box-shadow: 2px 3px 4px rgba(0, 247, 255, 0.452),inset 5px 6px 7px rgba(255, 0, 140, 0.562); }</style><div class=“box”></div>效果图:我们通过上图可以看到,虚线是盒子的位置,粉色阴影是inset属性设置的,所以是内阴影,浅蓝色是直接设置的外阴影,效果一目了然。6. 背景背景在CSS3中也得到很大程度的增强,比如背景图片尺寸、背景裁切区域、背景定位参照点、多重背景等。6.1 background-size通过background-size设置背景图片的尺寸,就像我们设置img的尺寸一样,在移动Web开发中做屏幕适配应用非常广泛。其参数设置如下:可以设置长度单位(px)或百分比(设置百分比时,参照盒子的宽高)设置为cover时,会自动调整缩放比例,保证图片始终填充满背景区域,如有溢出部分则会被隐藏。设置为contain会自动调整缩放比例,保证图片始终完整显示在背景区域。6.2 background-origin通过background-origin可以设置背景图片定位(background-position)的参照原点。其参数设置如下:border-box以边框做为参考原点;padding-box以内边距做为参考原点;content-box以内容区做为参考点;示例代码:<style type=“text/css”> .box1,.box2,.box3 { width: 200px; height: 200px; display: inline-block; margin: 50px 30px; border: 10px dashed aquamarine; padding: 10px; background-image: url(bg.jpg); background-repeat: no-repeat; } .box1{ background-origin: padding-box; } .box2{ background-origin: content-box; } .box3{ background-origin: border-box; }</style><div class=“box1”></div><div class=“box2”></div><div class=“box3”></div>效果图:6.3 background-clip通过background-clip,可以设置对背景区域进行裁切,即改变背景区域的大小。其参数设置如下:border-box裁切边框以内为背景区域;padding-box裁切内边距以内为背景区域;content-box裁切内容区做为背景区域;6.4 多背景以逗号分隔可以设置多背景,可用于自适应布局。在一个盒子里可以设置多个背景图片,通过背景定位的功能将两张图片组装起来。示例代码:<style type=“text/css”> .box { width: 320px; height: 410px; margin: 50px auto; background: url(head.jpg) no-repeat left top, url(foot.jpg) no-repeat left bottom; background-size: contain; background-color: #ccc; }</style><div class=“box”></div>效果图:从效果图中我们可以看到,在盒子里面设置了两张背景图,分别是上面一部分,下面一部分。这里故意给盒子高度拉长了一点,并且设置了一个灰色的背景,为的就是大家能够清楚的看到上下两部分的背景图。7. 渐变渐变是CSS3当中比较丰富多彩的一个特性,通过渐变我们可以实现许多炫丽的效果,有效的减少图片的使用数量,并且具有很强的适应性和可扩展性。7.1 线性渐变linear-gradient线性渐变指沿着某条直线朝一个方向产生渐变效果。1、必要的元素:借助Photoshop总结得出线性渐变的必要元素a、方向b、起始色c、终止色d、渐变距离2、关于方向通过具体的方位词指定to leftto rightto topto bottom通过角度改变渐变的方向0°,从下往上渐变90°,从左向右渐变示例代码:<style type=“text/css”> .box { width: 400px; height: 150px; margin: 100px auto; / 线性渐变 / background-image: linear-gradient( /渐变的方向/ 45deg, /渐变开始的颜色/ #88f5ea, /渐变结束的颜色/ #d36be7 ); }</style><div class=“box”></div>效果图:3、渐变范围如果不设置范围,默认渐变的范围是父盒子的宽度,如果通过background-size设置宽度的话,渐变范围即为设置的宽度。<style> .box { width: 500px; height: 100px; margin: 100px auto; background-image: linear-gradient( 135deg, yellow 20%, black 20%, black 40%, yellow 40%, yellow 60%, black 60%, black 80%, yellow 80%, yellow ); background-size: 66px 100px; }</style><div class=“box”></div>效果图:7.2 径向渐变radial-gradient径向渐变指从一个中心点开始沿着四周产生渐变效果。1、必要的元素:a、辐射范围即圆半径b、中心点 即圆的中心c、渐变起始色d、渐变终止色e、渐变范围2、关于中心点中心位置参照的是盒子的左上角,例如:<style> #div{ width:200px; height:200px; background: radial-gradient( / 100px是渐变辐射的范围 0 0 指的是圆心在盒子的左上角 / 100px at 0 0, /渐变起始色/ orange, /渐变终止色/ #ff4500 ) }</style><div id=“box”></div>示例代码:镜像渐变画个球<style type=“text/css”> * { margin: 0; padding: 0; } html,body { width: 100%; height: 100%; background-color: #ccc; } .box { width: 400px; height: 400px; background-color: #fff; margin: 50px auto; border-radius: 50%; background-image: radial-gradient( 300px at 100px 100px, rgba(0,0,0,.1), rgba(0,0,0,.8) ); }</style><div class=“box”></div>效果图:8. 过渡过渡是CSS3中具有颠覆性的特征之一,可以实现元素不同状态间的平滑过渡(补间动画),经常用来制作动画效果。8.1 帧动画通过一帧一帧的画面按照固定顺序和速度播放,如电影胶片。示例代码:<!– baidu.png这个背景图由64张图片横向组成,我们通过动态改变图片的位置,实现动画效果–><style> body { margin: 0; padding: 0; background-color: #F7F7F7; } .logo { width: 270px; height: 129px; margin: 100px auto; background-image: url(./baidu.png); background-position: 0 0; }</style><div class=“logo”></div><script> var logo = document.querySelector(’.logo’); var offset = -270; var n = 0; setInterval(function () { n++; logo.style.backgroundPosition = offset * n + ‘px 0px’; if(n >= 64) n = 0; },100);</script>效果图:这里不做详细了解,主要是为了区分与补间动画的区别。8.2 补间动画自动完成从起始状态到终止状态的的过渡。语法:transition当前元素只要有“属性”发生变化时,可以平滑的进行过渡,并不仅仅局限于hover状态。transition-property设置过渡属性/设置哪些属性要参加到动画效果中/transition-property: all;transition-duration设置动画过渡执行时间transition-duration: 2s;transition-timing-function设置过渡速度类型transition-timing-function:linear; / ease| ease-in | ease-out | ease-in-out | linear; /transition-delay设置过渡延时/1s后,过渡动画开始过渡/transition-delay: 1s;连写:/ 属性 执行时间 延时时间 过渡类型*/transition: all 2s 1s linear;示例代码:<style type=“text/css”> .box { width: 250px; height: 250px; background-color: pink; margin: 100px auto; transition: all 2s 1s linear; } .box:hover { width: 200px; height: 200px; border-radius:50%; background-color: rgb(25, 221, 247); }</style><div class=“box”></div>效果图:我们可以看到,触发hover事件的时候延迟1s后开始执行动画。示例代码:过渡的实际应用<style type=“text/css”> html,body { margin: 0; padding: 0; width: 100%; height: 100%; } .box { width: 100%; height: 100%; background-color: #eee; } .l_box { float: left; width: 234px; height: 300px; margin: 100px 50px; cursor: pointer; transition: all 0.5s linear; } .l_box:hover { box-shadow: -2px -2px 20px #777; margin-top: 90px; } .r_box { width: 234px; height: 300px; float: left; margin: 100px 0px; background-color: #fff; text-align: center; position: relative; } .cover { position: absolute; bottom: 0; height: 0px; width: 234px; background-color: orange; transition: all 1s linear; } .r_box:hover .cover { height: 100px; }</style><div class=“box”> <div class=“l_box”> <img src=“img/1.jpg” alt=""> </div> <div class=“r_box”> <img src=“img/2.jpg” alt=""> <div class=“cover”></div> </div></div>效果图:9. 2D转换转换(transform)是CSS3中具有颠覆性的特征之一,可以实现元素的位移、旋转、变形、缩放,甚至支持矩阵方式,配合即将学习的过渡和动画知识,可以取代大量之前只能靠Flash才可以实现的效果。9.1 位移CSS3中,通过translate属性对元素进行位移。移动translate(x, y)可以改变元素的位置,x、y可为负值;a、移动位置相当于自身原来位置b、y轴正方向朝下c、除了可以像素值,也可以是百分比,相对于自身的宽度或高度transform: translate(100px, 30px);示例代码<style type=“text/css”> .line { height: 200px; background-color: pink; } .box { width: 100px; height: 100px; background-color: rgb(30, 230, 245); transition: all 1s linear; } .line:hover .box { /* 位移 / transform: translate(100px, 30px); }</style><div class=“line”> <div class=“box”></div></div>效果图:我们可以看到,鼠标移上去之后,蓝色盒子,分别向左和向下移动了一段距离。9.2 缩放缩放scale(x, y)可以对元素进行水平和垂直方向的缩放,x、y的取值可为小数;/宽和高都放大1倍/transform: scale(1.5);示例代码:<style type=“text/css”> .box { width: 200px; height: 200px; background-color: pink; margin: 50px auto; transition: all 2s linear; } .box:hover { / 缩放 / transform: scale(0.5); }</style><div class=“box”></div>效果图:9.3 旋转旋转rotate(deg)可以对元素进行旋转,正值为顺时针,负值为逆时针;a、当元素旋转以后,坐标轴也跟着发生的转变b、调整顺序可以解决,把旋转放到最后/ 顺时针旋转 90度 /transform: rotate(90deg);示例代码:<style type=“text/css”> .box { width: 200px; height: 200px; background-color: #0df3cd; margin: 100px auto; transition: all 2s linear; } .box:hover { transform: rotate(-90deg); }</style><div class=“box”></div>效果图:旋转原点:默认情况下,旋转是按照元素的中心点旋转的,但是我们可以手动设置元素旋转的中心点。transform-origin: 30px 40px;示例代码:<style type=“text/css”> .box { width: 150px; height: 150px; background-color: cyan; margin: 100px auto; transition: all 1s linear; / 设置旋转原点位置 / / transform-origin: left top; / transform-origin: 30px 40px; } .box:hover { transform: rotate(90deg); }</style><div class=“box”></div>效果图:示例代码:扑克牌<style type=“text/css”> * { margin: 0; padding: 0; } .box { width: 310px; height: 438px; border: 1px solid #ccc; margin: 50px auto; position: relative; } .box img { position: absolute; transform-origin:bottom; transition: all 2s linear; } .box:hover img:nth-child(1) { transform: rotate(10deg); } .box:hover img:nth-child(2) { transform: rotate(20deg); } .box:hover img:nth-child(3) { transform: rotate(30deg); } .box:hover img:nth-child(4) { transform: rotate(40deg); } .box:hover img:nth-child(5) { transform: rotate(50deg); } .box:hover img:nth-child(6) { transform: rotate(60deg); }</style><div class=“box”> <img src=“img/pk1.png” alt=""> <img src=“img/pk1.png” alt=""> <img src=“img/pk1.png” alt=""> <img src=“img/pk1.png” alt=""> <img src=“img/pk1.png” alt=""> <img src=“img/pk1.png” alt=""></div>效果图:9.4 倾斜倾斜skew(deg, deg)可以使元素按一定的角度进行倾斜,可为负值,第二个参数不写默认为0。transform: skew(30deg,30deg);示例代码:<style type=“text/css”> .box { width: 150px; height: 150px; background-color: cyan; margin: 100px auto; transition: all 2s linear; } .box:hover { / 倾斜 / transform: skew(30deg, 30deg); }</style><div class=“box”></div>效果图:9.5 矩阵矩阵matrix()把所有的2D转换组合到一起,需要6个参数。transform-origin可以调整元素转换的原点,但是对于transform: translate(x,y)没有影响。我们可以同时使用多个转换,其格式为:transform: translate() rotate() scale() …等,其顺序会影转换的效果。详细可参见10. 3D 转换10.1 3D 坐标轴用X、Y、Z分别表示空间的3个维度,三条轴互相垂直。如下图:网格状的正方形,可以想象成是我们的电脑桌面2D平面。在 3D 转换中,前面 2D 转换的属性在这都可以使用:位移transform:translate(100px,100px,100px);旋转transform:rotate(30deg,30deg,30deg);缩放transform:scale(2,0.5,0.5);倾斜transform:skew(30deg,30deg,30deg);在3D转换中,一定要加上一个透视属性,才能在电脑2D平面中显示出3D的效果,透视属性请看下章。10.2 透视(perspective)电脑显示屏是一个2D平面,图像之所以具有立体感(3D效果),其实只是一种视觉呈现,通过透视可以实现此目的。perspective通过透视产生的3D效果,只是视觉呈现而已,并不是真正的3d立体的盒子;就是近大远小的效果。透视的概念其实很简单,就是“近大远小”。举个例子:在2D转换的时候,我们知道一个translate属性,他分别可以设置向左向右或者向上向下平移,但是却不能向里面或外面平移。<style> .box{ width: 550px; height: 150px; margin: 100px auto; padding: 6px; border: 1px dashed #ccc; } .box li{ float: left; width: 150px; height: 150px; padding: 0; list-style: none; margin-right: 50px; transition: all 0.5s linear; } .box li:first-child{ background: salmon; } .box li:nth-child(2){ background: deepskyblue; } .box li:last-child{ background: khaki; margin-right: 0px; } / 第一个盒子向X轴的负方向移动100px / .box li:first-child:hover{ transform: translateX(-100px); } / 第二个盒子向Y轴的正方向移动100px / .box li:nth-child(2):hover{ transform: translateY(100px); } / 第三个盒子Z轴的负方向移动100px / .box li:last-child:hover{ transform: translateZ(-100px); }</style><ul class=“box”> <li></li> <li></li> <li></li></ul>效果图:没有加透视属性的时候,因为z轴是垂直电脑平面射出来的,translateZ是看不出效果的。如何设置透视属性?给当前元素的直接父元素添加perspective: 800px;透视属性,注意这个值可以是随意的,但是最佳展现距离是600px~1000px。<style> .box{ width: 550px; height: 150px; margin: 100px auto; padding: 6px; border: 1px dashed #ccc; / 给变换的 li 的直接父元素 ul 添加透视属性 perspective / perspective: 800px; } .box li{ float: left; width: 150px; height: 150px; padding: 0; list-style: none; margin-right: 50px; transition: all 0.5s linear; } .box li:first-child{ background: salmon; } .box li:nth-child(2){ background: deepskyblue; } .box li:last-child{ background: khaki; margin-right: 0px; } / 第一个盒子向X轴的负方向移动100px / .box li:first-child:hover{ transform: translateX(-100px); } / 第二个盒子向Y轴的正方向移动100px / .box li:nth-child(2):hover{ transform: translateY(100px); } / 第三个盒子Z轴的负方向移动100px / .box li:last-child:hover{ transform: translateZ(-100px); }</style><ul class=“box”> <li></li> <li></li> <li></li></ul>效果图:如图所示,在ul加上透视属性后,第三个盒子向着z轴的负方向移动了100px。透视可以将一个2D平面,在转换的过程当中,呈现3D效果。(没有perspective,便“没有”Z轴)并非任何情况下都需要透视效果。10.3 3D呈现(transform-style)什么是3D呈现呢?不要与前面的透视搞混,透视只能使一个物体呈现近大远小的状态,不能呈现出一个立体感的东西,比如我在页面上用六个面组成一个正方形,通过透视你可能只能改变他的远近距离,并不能让他看起来像个立体的盒子,所以这里需要用到另一个属性:transform-style。transform-style: preserve-3d | flatflat:所有子元素在2D平面呈现preserve-3d:保留3D空间必须设置在父元素身上并且父元素有转换(就是有变形)并且子元素也得有转换(变形)才能看得到效果。1、示例代码:正方体<style type=“text/css”> * { margin: 0; padding: 0; } .box { width: 200px; height: 200px; margin: 150px auto; position: relative; / 透视 / / perspective: 1000px; / / 转为立方体 / transform-style: preserve-3d; transform: rotateY(45deg) rotateX(30deg); } .box>div { position: absolute; width: 100%; height: 100%; } .left { background-color: pink; transform: rotateY(-90deg) translateZ(150px); } .right { background-color: green; transform: rotateY(90deg) translateZ(150px); } .top { background-color: orange; transform: rotateX(90deg) translateZ(150px); } .bottom { background-color: blue; transform: rotateX(-90deg) translateZ(150px); } .before { background-color: red; transform: translateZ(150px); } .back { background-color: greenyellow; transform: translateZ(-150px); }</style><div class=“box”> <div class=“left”></div> <div class=“right”></div> <div class=“top”></div> <div class=“bottom”></div> <div class=“before”></div> <div class=“back”></div></div>效果图:2、示例代码:3D 导航<style type=“text/css”> * { margin: 0; padding: 0; list-style: none; } nav { width: 600px; height: 60px; line-height: 60px; margin: 200px auto; } li { height: 60px; width: 150px; float: left; position: relative; transform-style: preserve-3d; transition: all 0.5s ease-in; } span { position: absolute; width: 150px; height: 60px; display: block; color: #fff; text-align: center; } span:first-child{ background-color: #ff287a; transform: rotateX(90deg) translateZ(30px); } span:last-child{ transform: translateZ(30px); background-color: #00bdab; } li:hover { transform: rotateX(-90deg); }</style><nav> <ul> <li> <span>Home</span> <span>主页</span> </li> <li> <span>Menu</span> <span>菜单</span> </li> <li> <span>Admin</span> <span>管理</span> </li> <li> <span>About</span> <span>关于我们</span> </li> </ul></nav>效果图:10.4 3D呈现案例:3D轮播图1、普通版 3D 轮播图实现思路:通过CSS3中transform-style: preserve-3d的概念,将视图设置成3D展示模式;四张图片,分别设置其绕着X轴旋转的角度,分别对应四个立体面;将旋转好的图片沿着Z轴平移盒子宽度的一半;定义一个全局变量num,用来记录按钮点击的次数,当当按动按钮的时候,让ul旋转num90°;示例代码:<style> * { padding: 0; margin: 0; list-style-type: none; } .box { width: 960px; height: 540px; /* border: 5px solid #999; / margin: 150px auto; } .box ul { width: 960px; height: 540px; position: relative; transform-style: preserve-3d; transition: transform .6s; } .box ul li { width: 100%; height: 100%; position: absolute; left: 0; top: 0; } img { width: 100%; height: 100%; } .box ul li:nth-child(1) { transform: rotateX(90deg) translateZ(270px); } .box ul li:nth-child(2) { transform: rotateX(-90deg) translateZ(270px); } .box ul li:nth-child(3) { transform: rotateX(180deg) translateZ(270px); } .box ul li:nth-child(4) { transform: translateZ(270px); } .btn { width: 1080px; margin: 0 auto; position: relative; height: 0px; top: -470px; } .btn button { width: 40px; height: 80px; border-radius: 10px; background: rgba(0, 0, 0, 0.2); border: none; outline: none; font: 900 24px/80px ‘宋体’; color: #fff; } .btn button:frist-child { float: left; } .btn button:last-child { / border-radius: 0 10px 10px 0; / float: right; }</style><div class=“box”> <ul> <li><img src="./imgs/1.jpg" alt=""></li> <li><img src="./imgs/2.jpg" alt=""></li> <li><img src="./imgs/3.jpg" alt=""></li> <li><img src="./imgs/4.jpg" alt=""></li> </ul></div><div class=“btn”> <button>&lt;</button> <button>&gt;</button></div><script type=“text/javascript”> var btn = document.querySelectorAll(‘button’); var box = document.querySelector(’.box’); var _ul = box.querySelector(‘ul’); var num = 0; // btn 获取到的是一个伪数组 btn[1].onclick = function () { num++; _ul.style.transform = ‘rotateX(’ + num * 90 + ‘deg)’ } btn[0].onclick = function () { num–; _ul.style.transform = ‘rotateX(’ + num * 90 + ‘deg)’; }</script>效果图:2、切割版 3D 轮播图实现思路:结构上将之前定义好的ul重复四次;设定延时,整个动画执行时间是0.8s,设定每一个ul延迟执行0.2s,即第一个延时0s,第二个延时0.2s,第三个延时0.4s,第四个延时0.6s;其余步骤同上,着重强调的是,相对于上面的案例,本案例给按钮加了限制,监听第一次所有的ul旋转结束之后,按钮才能再一次被点击。示例代码:<style> * { padding: 0; margin: 0; list-style-type: none; } .box { width: 960px; height: 540px; margin: 150px auto; display: flex; } .box ul { width: 960px; height: 540px; position: relative; transform-style: preserve-3d; transition: transform .8s; } .box ul li { width: 100%; height: 100%; position: absolute; left: 0; top: 0; overflow: hidden; } img { width: 960px; height: 540px; } / 设定延时 / .box ul:nth-child(1) { transition-delay:0s; } .box ul:nth-child(2) { transition-delay:.2s; } .box ul:nth-child(3) { transition-delay:.4s; } .box ul:nth-child(4) { transition-delay:.6s; } / 拼凑图片 / .box ul:nth-child(2) img { margin-left: -240px; } .box ul:nth-child(3) img { margin-left: -480px; } .box ul:nth-child(4) img { margin-left: -720px; } .box ul li:nth-child(1) { transform: rotateX(90deg) translateZ(270px); } .box ul li:nth-child(2) { transform: rotateX(-90deg) translateZ(270px); } .box ul li:nth-child(3) { transform: rotateX(180deg) translateZ(270px); } .box ul li:nth-child(4) { transform: translateZ(270px); } .btn { width: 1080px; margin: 0 auto; position: relative; height: 0px; top: -470px; } .btn button { width: 40px; height: 80px; border-radius: 10px; background: rgba(0, 0, 0, 0.2); border: none; outline: none; font: 900 24px/80px ‘宋体’; color: #fff; } .btn button:frist-child { float: left; } .btn button:last-child { / border-radius: 0 10px 10px 0; */ float: right; }</style><div class=“box”><ul> <li><img src="./imgs/1.jpg" alt=""></li> <li><img src="./imgs/2.jpg" alt=""></li> <li><img src="./imgs/3.jpg" alt=""></li> <li><img src="./imgs/4.jpg" alt=""></li></ul><ul> <li><img src="./imgs/1.jpg" alt=""></li> <li><img src="./imgs/2.jpg" alt=""></li> <li><img src="./imgs/3.jpg" alt=""></li> <li><img src="./imgs/4.jpg" alt=""></li></ul><ul> <li><img src="./imgs/1.jpg" alt=""></li> <li><img src="./imgs/2.jpg" alt=""></li> <li><img src="./imgs/3.jpg" alt=""></li> <li><img src="./imgs/4.jpg" alt=""></li></ul><ul> <li><img src="./imgs/1.jpg" alt=""></li> <li><img src="./imgs/2.jpg" alt=""></li> <li><img src="./imgs/3.jpg" alt=""></li> <li><img src="./imgs/4.jpg" alt=""></li></ul></div><div class=“btn”><button>&lt;</button><button>&gt;</button></div><script type=“text/javascript”> var btn = document.querySelectorAll(‘button’); var box = document.querySelector(’.box’); var _ul = box.querySelectorAll(‘ul’); var num = 0; var flag = true; // btn 获取到的是一个伪数组 btn[1].onclick = function () { if (flag) { flag = false; num++; for (var i = 0; i < _ul.length; i++) { _ul[i].style.transform = ‘rotateX(’ + num * 90 + ‘deg)’; } // 监听最后一个transition事件结束的时候 将flag 置为 true 防止重复点击 _ul[_ul.length - 1].addEventListener(’transitionend’, function () { flag = true; }) } } btn[0].onclick = function () { if (flag) { flag = false; num–; for (var i = 0; i < _ul.length; i++) { _ul[i].style.transform = ‘rotateX(’ + num * 90 + ‘deg)’; } _ul[_ul.length - 1].addEventListener(’transitionend’, function () { flag = true; }) } }</script>效果图:10.5 backface-visibilitybackface-visibility属性定义当元素不面向屏幕时是否可见。如果在旋转元素不希望看到其背面时,该属性很有用。有两个属性:1、visible背面是可见的2、hidden背面是不可见的 ...

November 26, 2018 · 11 min · jiezi