Giter Club home page Giter Club logo

h5skills's People

Contributors

leeenx avatar liqinuo avatar waileung avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

h5skills's Issues

文字超出省略号

单行文字超出省略号

.example {
    width: 200px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-scaple: nowrap;
}

多行文字超出省略号

.example {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2; /* 指定超过2行省略号 */
    text-overflow: ellipsis;
    overflow: hidden;
}

实现渐变文字与镂空文字

<p class="gradient_txt">渐变文字渐变文字渐变文字渐变文字</p>
.gradient_txt {
    background-color: #890ffa;
    background-image: gradient(linear, left top, right top, from(#890ffa), to(#d80078));
    background-clip: text;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}
<p class="hollow_txt">镂空文字镂空文字镂空文字镂空文字</p>
.hollow_txt {
    font-size: 50px;
    -webkit-text-stroke: 1px #000;
    -webkit-text-fill-color: transparent;
}

flex 属性

flex

flex属性是 flex-grow、flex-shrink、flex-basis 三者属性的简写,后两个属性可忽略。

默认值为 flex: 0 1 auto:。意思为,有剩余空间也不会放大、空间不足则自动等比例缩小、此 flex 元素初始所占宽度为元素本来的宽度。

还有两个快捷值,auto(1 1 auto)和none(0 0 auto)

flex-grow:它定义的是元素按比例平分父元素剩余的空间。

公式:
	第一步:计算剩余空间
		父容器宽度 - 所有子元素的初始宽度之和(这里子元素的初始宽度为 flex-basis 属性的值,而 flex-basis 属性的值默认为 auto,即此子元素自身的宽度) 
	第二步:计算每一份 flex-grow 的宽度。
		上一步得到的剩余空间 /(所有子元素 flex-grow 值的和)
	第三步:所有 flex-grow 值非0的子元素瓜分剩余空间
		此元素放大后宽度 = 此元素原本宽度 +( flex-grow 值 * 每一份 flex-grow 的宽度)

flex-shrink:它定义的是当空间不足(没有设置换行)时,元素会等比例缩小。

公式:
	第一步:计算超出空间
		所有子元素的初始宽度之和(flex - basis 属性的值)- 父容器宽度
	第二步:计算每一份 flex-shrink 宽度。
		上一步得到的超出空间 / (所有子元素 flex-shrink 值的和)
	第三步:各子元素分担一下压力。。
		此元素缩小后宽度 = 此元素原本宽度 -( flex-shrink 值 * 每一份 flex-shrink 的宽度)

按我们 _function.scss 组件里的 %flex 的 flex: 1;。意思为会按子元素本身大小算作子元素 flex 宽度,父元素空间有剩余则平分剩余空间来放大,父元素空间不足则平分空间缩小。

所以当所有的子元素宽度相等时,无论是1%宽度还是100%宽度或任意宽度时,经计算后呈现的效果都是一致的,平分父元素空间。

表单样式小技巧集合

select下拉框 文字居右

qq20160823-0_2x

select {
    direction: rtl;
    -webkit-direction: rtl;
}

input placeholder改颜色

input::-webkit-input-placeholder {color: #AAAAAA;}
input:focus::-webkit-input-placeholder {color: #EEEEEE;}

iOS input 组件默认样式去除

-webkit-appearance: none;

通过 :empty 选择器区分样式

:empty 选择器匹配没有子元素(包括文本节点)的每个元素。

这里举个小红点的例子:

如图所示,小红点有内容以及无内容的样式差异,按照常规的处理方式,我们一般是通过类名区分,但是我们可以简单通过:empty选择器区分开。

<div class="jd"><i>3</i></div>
.jd i:empty {
    // 无内容的小红点样式
}
.jd i:not(:empty) {
    // 有内容的小红点样式
}

判断用户手机旋屏

判断用户屏幕大概有这几种方法:

1、CSS Media Queries

内联样式

@media screen and (orientation:portrait) {
    //竖屏
}
@media screen and (orientation:landscape) {
    //横屏
}

外联样式

<!-- 竖屏 -->
<link rel="stylesheet" media="all and (orientation:portrait)" href="..." />
<!-- 横屏 -->
<link rel="stylesheet" media="all and (orientation:landscape)" href="..." />

2、window.matchMedia()

var mql = window.matchMedia("(orientation: portrait)");
function onMatchMeidaChange(mql){
    if(mql.matches) {
        // 竖屏
    }else {
        // 横屏
    }
}
onMatchMeidaChange(mql);
mql.addListener(onMatchMeidaChange);

3、window.innerHeight/window.innerWidth

function detectOrient(){
    if(window.innerHeight >= window.innerWidth) {
        // 竖屏
    }else {
        // 横屏
    }
}
detectOrient();
window.addEventListener('resize',detectOrient);

4、window.orientation

switch(window.orientation) {
    case 0:
        displayStr += "Portrait";
        break;
    case -90:
        displayStr += "Landscape (right, screen turned clockwise)";
        break;
    case 90:
        displayStr += "Landscape (left, screen turned counterclockwise)";
        break;
    case 180:
        displayStr += "Portrait (upside-down portrait)";
        break;
}
function detectOrient(){
    if (Math.abs(window.orientation) === 90) {
        // 横屏
    } else {
        // 竖屏
    }
}
detectOrient();
window.addEventListener('orientationchange',detectOrient);

更详细的内容请移步至:探讨判断横竖屏的最佳实现

实现绝对底部(Sticky Footer)的几种方案

所谓 “Sticky Footer”,指的是:如果页面内容不足够长时,页脚固定在浏览器窗口的底部;如果内容足够长时,页脚固定在页面的最底部。
总而言之,就是页脚一直处于最底,效果大致如图所示:

假设我们页面的 HTML 结构是这样:

<div class="wrapper">
    <div class="content"><!-- 页面主体内容区域 --></div>
    <div class="footer"><!-- 需要做到 Sticky Footer 效果的页脚 --></div>
</div>

实现方案一:absolute

通过绝对定位处理应该是常见的方案,只要使得页脚一直定位在主容器预留占位位置。

html, body {
    height: 100%;
}
.wrapper {
    position: relative;
    min-height: 100%;
    padding-bottom: 50px;
    box-sizing: border-box;
}
.footer {
    position: absolute;
    bottom: 0;
    height: 50px;
}

这个方案需指定 html、body 100% 的高度,且 content 的 padding-bottom 需要与 footer 的 height 一致。

实现方案二:calc

通过计算函数 calc 计算(视窗高度 - 页脚高度)赋予内容区最小高度,不需要任何额外样式处理,代码量最少、最简单。

.content {
    min-height: calc(100vh - 50px);
}
.footer {
    height: 50px;
}

如果不需考虑 calc() 以及 vh 单位的兼容情况,这是个很理想的实现方案。
同样的问题是 footer 的高度值需要与 content 其中的计算值一致。

实现方案三:table

通过 table 属性使得页面以表格的形态呈现。

html, body {
    height: 100%;
}
.wrapper {
    display: table;
    width: 100%;
    min-height: 100%;
}
.content {
    display: table-row;
    height: 100%;
}

需要注意的是,使用 table 方案存在一个比较常见的样式限制,通常 margin、padding、border 等属性会不符合预期。
笔者不建议使用这个方案。当然,问题也是可以解决的:别把其他样式写在 table 上。

实现方案四:Flexbox

Flexbox 是非常适合实现这种效果的,使用 Flexbox 实现不仅不需要任何额外的元素,而且允许页脚的高度是可变的。

虽然大多数 Flexbox 布局常用于水平方向布局,但别忘了实际上它也可用于垂直布局,所以你需要做的是将垂直部分包装在一个 Flex 容器中,并选择要扩展的部分,他们将自动占用其容器中的所有可用空间。

html {
    height: 100%;
}
body {
    min-height: 100%;
    display: flex;
    flex-direction: column;
}
.content {
    flex: 1;
}

需要注意的是想要兼容各种系统设备,需要兼顾 flex 的兼容写法。

元素使用 display: inline-block 存在多余的间距

正常情况下我们只需在父容器加 font-size: 0; 即可间距解决问题,但如果设置了 -apple-system 字体在 iOS 查看时会发现间距依然会存在,解决的方法是给父容器设置 font-size: 0;font-family: Helvetica; 解决,另外记得将子容器的 font-family 设回默认值。

使用前:(每个 <li> 元素有 margin-right: 10px;

使用前

使用后:(间距恢复正常)

使用后

图标跟随文字,文字超出隐藏

b

实现如图效果,标签一直跟随在文字后面。如果文字超出一行,文字裁断省略,标签保留在最右。

HTML:

<div class="jd">
    <span>小米官方旗舰店</span>
    <em>京东自营</em>
 </div>

CSS:

.jd {
    margin-right: 60px; /* 对应是标签的宽度 */
    white-space: nowrap;
    span, em {
        display: inline-block;
        vertical-align: middle;
    }
    span {
        max-width: 100%;
        /* 单行文字超出省略 */
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    em {
        width: 60px; /* 标签的宽度 */
    }
}

X5内核同层视频播放器说明

X5内核最新版本支持了全屏播放视频的功能,效果如下,这是在线demo,有安卓的同学可以试试:

使用条件

  • 微信/手Q浏览器、腾讯浏览器下
  • 安卓平台,TBS内核版本在036849以上
  • 页面有video

全屏后界面特点

  1. 顶部安卓状态栏透明、消失
  2. 顶部菜单栏会用半透明渐变代替,覆盖在视频上
  3. 没有顶部菜单的话也就没有网页标题,需要用dom去模拟
  4. 全屏后页面是单屏,页面不能滚动
  5. 页面需要展示的dom必须悬浮在video的区域上,否则不会被展示

开启方法

  1. 判断浏览器ua,如果有" TBS/036849 "字符且版本号大于036849

  2. 在video标签加上x5-video-player-type="h5"属性

<!-- 视频播放的时候会自动进入全屏状态 -->
<video id="test_video" src="xxx" x5-video-player-type="h5" x5-video-player-fullscreen="true"/>
<!-- x5-video-player-fullscreen加不加我看不出什么区别,官方文档也没有说得很详细 -->

全屏的时候会触发以下事件:

window.onresize = function(){
    //do something
}

注意:弹出/收起键盘、退出全屏 的时候也会触发该事件,注意兼容

调试

不用特别设置,在微信浏览器里打开localhost页面即可

小技巧

  • 经测试,如果视频的opacity为0界面也会进入全屏,所以如果非视频类活动也想用这个特性的话可以用一个透明的、静音的、短的视频在低层级不断循环播放,高层放你需要的dom
  • 连续多个视频播放时不宜用同层播放器,因为切换视频时会退出同层播放器界面然后再进入,体验会不好

相关资料

腾讯官方文档 https://x5.tencent.com/tbs/guide/video.html

js日期类常用操作

  1. new Date()的使用方法有:

    • 不接收任何参数:返回当前时间;
    • 接收一个参数x: 返回1970年1月1日 + x毫秒的值。
    • new Date(1, 1, 1)返回1901年2月1号。
    • new Date(2016, 1, 1)不会在1900年的基础上加2016,而只是表示2016年2月1号。
  2. 使用new Date(time) 将时间转换成 Date 对象 ,便于与后台接口通信。注意:time格式需要为 1999/12/31 23:59 (不能为1999-12-30 23:43),否则在一些机型下可能会报错。

new Date()转换之后的数据,可以直接使用下面的api
new Date(x).getDay  //获取一周中的某一天 (0 ~ 6)。
new Date(x).getMonth()+1   //获取月份
new Date(x).getDate  //获取日期
new Date(x).getHours()  //获取小时
new Date(x).getMinutes()  //获取分钟
new Date(x).toLocaleDateString()); // 转换为本地日期格式,视环境而定,输出:2017年07月04日
new Date(x).toLocaleString()); // 转换为本地日期和时间格式,视环境而定,输出:2017年07月04日 上午10:03:05
  1. javascript 没有原生提供但却经常需求使用的功能
  • 根据日期获取当前星期几
//参数  日期
getWeek(day) {
   const weekArr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
   return weekArr[day];
}
getWeek(new Date(x).getDay()) 
  • 获取某个时间+1个小时,直接对小时数进行加1可能会溢出,因此先转换成 Date 对象,再使用setHours 改变小时。
etime.setHours(etime.getHours()+1, etime.getMinutes());
  • 为了统一格式,返回日期是10以下,需在前面补0.
function getFull(n) {
    return (n > 9 ? '' : '0') + n;
}
var x = getFull(3);  //03
var y = getFull(11);   //11
  • 对时间进行格式转换
Date.prototype.Format = function (fmt) { //author: meizz
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
调用:
var time1 = new Date().Format("yyyy-MM-dd");
var time2 = new Date().Format("yyyy-MM-dd hh:mm:ss");

不定宽块级元素水平居中

在我们的传统方法中,对于这种不定宽要居中的元素,我们一般采取两层的结构,以下:

<div class="more_btn"><span>查看更多</span></div>
.more_btn {
    text-align: center;
}
.more_btn span {
    display: inline-block;
    padding: 0 30px;
    height: 40px;
    line-height: 40px;
    background: #4caf50;
    color: #fff;
    font-size: 14px;
    text-align: center;
    border-radius: 4px;
}

使用 width: fit-content 属性来简化结构

<div class="more_btn">查看更多</div>
.more_btn {
    margin: auto;
    padding: 0 30px;
    width: -webkit-fit-content;
    width: fit-content; /* 关键 : 宽度等于内容宽度 */
    height: 40px;
    line-height: 40px;
    background: #4caf50;
    color: #fff;
    font-size: 14px;
    text-align: center;
    border-radius: 4px;
}

伪元素建议使用双冒号

A pseudo-element is made of two colons (::) followed by the name of the pseudo-element.
This :: notation is introduced by the current document in order to establish a discrimination between pseudo-classes and pseudo-elements. For compatibility with existing style sheets, user agents must also accept the previous one-colon notation for pseudo-elements introduced in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and :after). This compatibility is not allowed for the new pseudo-elements introduced in this specification.

引自 Seletors Level 3

大概的意思是伪元素是由两个 :: 和伪元素组成的,引入两个 :: 的目的为了区分伪元素和伪类的,对于一些老旧的的项目,比如需要兼容 IE8 及以下的浏览器建议还是使用 :,对于不需要支持低版本浏览器和移动端项目的建议使用 :: 来做区分。

::after::before::first-letter::first-line::selection 等是伪元素
:active:hover:focus:visited:checked:disabled:empty 等称为伪类

CSS的版本

版本号

W3C明确表示,CSS没有版本只能等级(level)。

Cascading Style Sheets does not have versions in the traditional sense; instead it has levels. Each level of CSS builds on the previous, refining definitions and adding features. The feature set of each higher level is a superset of any lower level, and the behavior allowed for a given feature in a higher level is a subset of that allowed in the lower levels. A user agent conforming to a higher level of CSS is thus also conformant to all lower levels.

上文摘录自:https://www.w3.org/TR/2015/NOTE-css-2015-20151013/#css-levels

通常称呼 css版本 由level+revision(级别和修订号) 组合而来。例如:css 2.1,2是level号,1是修订号,表示是css2的第一个修订版本。
也就是说 1.0, 2.0 类似这样的版本号是不存在的,因为不可能有第0个修订版本。

css 1

css 1 只有一个版本: https://www.w3.org/TR/CSS1/

css第一个规范的地址:https://www.w3.org/TR/REC-CSS1-961217.html 很有意思,表格完全没有图片,像是dos时代的产品

css 2

css 2 目前有三个版本:css 2, css2.1, css2.2(css2.2是2016.04.12才出的修订版)

css 2.1 是 css 2的衍生版。

CSS 2.1 is derived from and is intended to replace CSS2.

来自:Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification

*注:css 2.2是level 2最新修订片,所以现在的主流是 2.1,关于 css 2.2 的介绍查看:https://www.w3.org/TR/CSS22/ *

css 3

css 3 只有一个版本:css 3。从css 3开始,css工作组开始引用模块化的管理方式,如下:

CSS Level 3 builds on CSS Level 2 module by module, using the CSS2.1 specification as its core. Each module adds functionality and/or replaces part of the CSS2.1 specification. The CSS Working Group intends that the new CSS modules will not contradict the CSS2.1 specification: only that they will add functionality and refine definitions. As each module is completed, it will be plugged in to the existing system of CSS2.1 plus previously-completed modules.

From this level on modules are levelled independently: for example Selectors Level 4 may well be completed before CSS Line Module Level 3. Modules with no CSS Level 2 equivalent start at Level 1; modules that update features that existed in CSS Level 2 start at Level 3.

文章摘录自:https://www.w3.org/TR/2015/NOTE-css-2015-20151013/#css

大意是:css 3的规范以独立的模块的形式存在,规范的修订以增量的存在(而不是以修订版的形式发布,潜台词是 css 3以后不会有修订版,即css 3.x永远不会存在)。

《css 快照2015》(即当前最新的css官方文档)的介绍里直白地说明了这点:

When the first CSS specification was published, all of CSS was contained in one document that defined CSS Level 1. CSS Level 2 was defined also by a single, multi-chapter document. However for CSS beyond Level 2, the CSS Working Group chose to adopt a modular approach, where each module defines a part of CSS, rather than to define a single monolithic specification. This breaks the specification into more manageable chunks and allows more immediate, incremental improvement to CSS.

css4

CSS Level 4 and beyond There is no CSS Level 4. Independent modules can reach level 4 or beyond, but CSS the language no longer has levels. ("CSS Level 3" as a term is used only to differentiate it from the previous monolithic versions.)

上文摘录自:CSS Snapshot 2015

css的级别有且只有三个 css1, css2, css3。现在或以后都不可能有css4,CSS 3做为专用术语用于与之前的版本做区别。

前端css字体调研

一、一些数据

先贴一些各种系统的默认字体数据:

ios:

ios9+中文:PingFang SC(苹方)

ios9+英文/数字:San Francisco

其他ios中文:STHeiTi(华文黑体)

其他ios英文/数字:Helvetica

mac:

10.5:华文黑体(STXihei/STHeiti)

10.6-10.10:黑体-简(Heiti SC),华文黑体的改进版;在10.6后自带了冬青黑Hiragino Sans GB,但不是默认字体

10.11:El Capitan后默认字体改为PingFang SC(苹方),英文/数字改为San Francisco

注意:电脑的字体和网页css字体没有必然关系,而且浏览器自身一般支持设定字体,贴出来仅供参考。

android:

中文:Droidsansfallback

英文/数字:Roboto(4.0+),Droid Sans(4.0以下)

二、分析

  • ios9+的css字体设置了-apple-system的话中文会调用苹方,英文/数字则调San Francisco;San Francisco在ios/mac上没有显式暴露出来(不能通过字体名字调用),需要通过这种方式调用;
  • 在不支持-apple-system的ios系统下会调用华文细黑的中文/英文/数字;
  • sans-serif在非ios9+下会调用华文细黑的中文/英文/数字;
  • sans-serif在ios9+下会调用苹方的中文/英文/数字;
  • sans-serif在安卓下会调用系统的中文/英文字体,一般不需要其他设置;
  • 总的来说sans-serif在手机系统下会调用默认中文字体的中文/英文/数字

顺便说一下几个字体的规律:

  • font-family从左到右查找字体,如果没有则顺序查找下一个;
  • 中文字体一般包括了英文/数字符号,英文字体不包括中文字体,即系统如果有一个声明的中文字体,则英文也会用该中文字体去展示;
  • 中文字体都有对应的英文名字、unicode写法,如“微软雅黑, Microsoft Yahei, \5FAE\8F6F\96C5\9ED1”,英文写法在可读性和适用性上更好;
  • Helvetica和Arial分别是mac(ios)和windows下最常见的无衬线西文字体;
  • 有一些软件会自带一些字体供其使用,如photoshop

字体预览demo:http://jdc.jd.com/demo/font_test/index.html

由于手机系统一般只有一种中文字体,所以定义css字体时一般只要考虑英文/数字的字体。以下是我总结出来可供参考的几个字体设置:

参考样式1:

//全部ios英文&数字统一Helvetica,ios9+中文苹方,其他ios中文华文细黑,安卓默认中英文
body {
    font-family: Helvetica,sans-serif;
}

参考样式2:

//ios9+中文苹方,英文/数字San Francisco,其他ios中文华文细黑,英文Helvetica,安卓默认中英文
body {
    font-family: -apple-system,Helvetica,sans-serif;
}

三、几个大站的css字体

下面的数据均在2016年5月25日采集。

站点 字体 ios9+ 其余ios 安卓
手机天猫 Helvetica,sans-serif 中文:苹方,英/数:Helvetica 中文:华文细黑,英/数:Helvetica 默认中英文字体
手机淘宝 sans-serif 中文:苹方,英/数:苹方 中文:华文细黑,英/数:华文细黑 默认中英文字体
h5微博timeline页面 PingFang-SC-Regular,Helvetica,sans-serif 中文:苹方,英/数:苹方 中文:华文细黑,英/数:Helvetica 默认中英文字体
微信文章页 -apple-system-font,"Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif 中文:苹方,英/数:San Francisco 中文:华文细黑,英/数:Helvetica Neue 默认中英文字体

其中微信文章页因为要兼容pc和mac,所以用了比较多字体。

四、参考资料

中文Web字体方案 z4rd/demo#3

alloyteam移动开发规范 http://alloyteam.github.io/Spirit/modules/Standard/#font

如何保证网页的字体在各平台都尽量显示为最高质量的黑体? https://www.zhihu.com/question/19911793

一个 mask-border 在安卓机的 hack 的发现之旅

mask-border 在安卓下失效的BUG

上周,同事反馈了一个使用 mask-border 在华为荣耀3c机子下矢效的BUG。由于本人写过关于 mask 与 mask-border 的文章: https://aotu.io/notes/2016/10/19/css3-mask/ ,并也是主张推广它的人,所以解决这个BUG是当务之急。

*注意了,本文的hack是针对 x5 内核而言 *

出问题的页面使用了我的文章里介绍的方法,但是却出现了如下BUG:

正常的显示是(ip6):

对应的关键代码如下:

.mask::after {
    content: ''; 
    position: absolute;
    top: 0;
    left: 0;
    width: 100px;
    height: 100px;
    z-index: 3;
    background-color: #ff0;
    pointer-events: none;
    mask-border: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8AQMAAAAAMksxAAAABlBMVEUAAADxVZe4xg/eAAAAAXRSTlMAQObYZgAAAFdJREFUKM9j+H/8Pwh8YGhwYWBgYFIQwMdo/3+AgcH+nwBDi4sDA4OSEjbGf7AaoIEsYF1KDJgMuBp85rQfB6qxswPazgAGw44B9SAdAhMRccTEMjxJAACiVld1wtGnfgAAAABJRU5ErkJggg==) 18/9px stretch;
}

经过遂节点排查,居然发现当页面节点比较多的情况下 mask-border 会失效 -_-|||,这感觉是个没有办法解决的BUG。

另一个BUG

我自己当前正好有一个年货二期的页面,今天(2016.12.26)在ip6在体验时,发现页面巨卡,但是在安卓下(华为荣耀3c)却又不卡!!!

通过定位发现是 drop-shadow 导致 iOS 下的卡顿,毕竟 drop-shadow 也是一个 filter,滤镜在 iOS 下是比较占资源的!!于是把这个 drop-shadow 去掉后解决了卡顿的BUG。

但是,我意外地发现:如果把滤镜去掉后,安卓下 mask-border 会失效
如下:

未去滤镜之前的样子如下:

drop-shadow 是一个 filter 那么我加个 blur(0) 是不是可以解决这个BUG?
答案是肯定的!!!

不过,这个 filter: blur(0) 需要放在 mask-border 的父节点上才可以生效。基于这点,针对 低端安卓机 写一个 hack:

.mask {
    filter: blur(0);
    transform: translate3d(0, 0, 0);
    @supports(-webkit-mask-repeat: repeat) {
        // 高端安卓 与 ios 能识别
        filter: none;
    }
}

.mask::after {
    content: ''; 
    position: absolute; 
    top: 0;
    left: 0;
    width: 100px;
    height: 100px;
    z-index: 3;
    background-color: #ff0;
    pointer-events: none;
    mask-border: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8AQMAAAAAMksxAAAABlBMVEUAAADxVZe4xg/eAAAAAXRSTlMAQObYZgAAAFdJREFUKM9j+H/8Pwh8YGhwYWBgYFIQwMdo/3+AgcH+nwBDi4sDA4OSEjbGf7AaoIEsYF1KDJgMuBp85rQfB6qxswPazgAGw44B9SAdAhMRccTEMjxJAACiVld1wtGnfgAAAABJRU5ErkJggg==) 18/9px stretch;
}

将这段代码放到 mask-border 失效的页面,成功解决BUG。

最终的hack

笔者在解决BUG的过程中发现,如果 mask-border 的父节点的结构比较复杂的话,单纯使用 filter 也不能解决问题,这个时间需要现加一个 3d 加速才可以完全解决问题,所以我写的hack如下:

%mask-border-hack {
    filter: blur(0);
    transform: translate3d(0, 0, 0);
    @supports(-webkit-mask-repeat: repeat) {
        // 高端安卓 与 ios 能识别
        filter: none;
    }
}

上面hack生效有个前提,mask-border不能使用3d加速

安全建议

由于观察的情况有限,也不能保证上面代码的 100%有效。我个人建议使用另一种比较保险的做法,就是 mask-border 放在一个单独的 div 下,这样子就可以 100% 保证上面 hack 生效。如下:

不过,我现在观察的情况有限,也不能保证上面代码的 100%有效。我个人建议使用另一种比较保险的做法,就是 mask-border 放在一个单独的 div 下,这样子就可以 100% 保证上面 hack 生效。如下:

<style>
.mask {
    filter: blur(0);
    transform: translate3d(0, 0, 0);
    @supports(-webkit-mask-repeat: repeat) {
        // 高端安卓 与 ios 能识别
        filter: none;
    }
}

.mask::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100px;
    height: 100px;
    z-index: 3;
    background-color: #ff0;
    pointer-events: none;
    mask-border: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8AQMAAAAAMksxAAAABlBMVEUAAADxVZe4xg/eAAAAAXRSTlMAQObYZgAAAFdJREFUKM9j+H/8Pwh8YGhwYWBgYFIQwMdo/3+AgcH+nwBDi4sDA4OSEjbGf7AaoIEsYF1KDJgMuBp85rQfB6qxswPazgAGw44B9SAdAhMRccTEMjxJAACiVld1wtGnfgAAAABJRU5ErkJggg==) 18/9px stretch;
}

</style>

<!-- 把box的::after转移动到 mask 的 ::after 中去,再加 mask 加一个hack  -->
<div class=“box”>
    <div class=“mask”></div>
</div>

探究iPhone不同显示模式device-width值变化

#探究iPhone不同显示模式device-width值变化

背景

  为了更好的大屏显示效果,苹果为 iPhone 6、iPhone 6s、iPhone 6 Plus、iPhone 7、iPhone 7 Plus 都新增了“放大显示”模式,并提示用户使用“标准”或者“放大”模式来获得更适合自己的显示效果。

  通过观察我们可以发现,iPhone 6 Plus 和 iPhone 7 Plus 在放大模式下的显示内容是和 iPhone 6、iPhone 6s、iPhone 7 的标准模式一样的,这意味着 iPhone 6、iPhone 6s、iPhone 7 放大模式的内容数量,和 iPhone 5s 的显示内容是几乎一致的。

zoomdisplaycompare

此时,设备视口宽度(device-width)、高度(device-height), 将变为与iphone 5s一致。具体,可以参照 paintcode 的信息图, 如下:

iphoneDevice

而判断iphone6plus等大屏条件手机如下:

if(window.screen.width >= 375 && window.devicePixelRatio == 3) || (window.screen.width >= 414 && window.devicePixelRatio == 3){
	//此为iphone大屏手机ip6p,ip7p
}

而无法准确判断区分iphone5、iphone5s与iphone6、iphone6s。

参考资料:

信不信由你!iPhone6屏幕宽度不一定是375px,iPhone6 Plus屏幕宽度不一定是414px

The Ultimate Guide To iPhone Resolutions

行内块元素如何无缝平铺

非safari环境

利用行内块元素(inline-block)可以比较优雅地替代浮动做块平铺显示,也可以避免浮动带来的一些负面影响。如下:

.container{
	width: 600px;
	height: 60px;
	margin: 0 auto;
	font-family: -apple-system;
}
span{
	display: inline-block;
	width: 60px;
	height: 60px;
	background: #333;
	color: #fff;
}
<div class="container">
	<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>
	<span>10</span>
</div>

然而,效果并不是想像的那样:

{{:blog:2016:04:1.png?200|}}

因为字体大小不为0,造成空白节点仍然地占用一个 字符串的空间!去空格的方法有两种:

方法一

让行内块元素之间紧挨着,不留空白可以解决这个问题,如下:

<div class="container">
	<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><span>10</span>
</div>

上面的方案显然不优雅。

方法二

一般情况下把container的font-size设置为0就可以解决问题了。如下:

.container{
	width: 600px;
	height: 60px;
	margin: 0 auto;
	font-size: 0;
	font-family: -apple-system;
}
span{
	display: inline-block;
	width: 60px;
	height: 60px;
	background: #333;
	color: #fff;
	font-size: 24px;
}

{{:blog:2016:04:2.png?200|}}

safari环境

一直都是使用 font-size: 0; 来做行内块无缝平铺的。最近,T4哥(姓李名伟亮号王子谥威廉I),发现需要使用font-size: 0; 在PC safari或iphone手机中不能解决无缝平铺。如下:

{{:blog:2016:04:3.png?200|}}

原因是font-family使用了 -apple-system造成的。解决的方法有3个:

方法一

与非safari环境一样。

方法二

使用letter-spacing取负值(-4px)来对冲因为-apple-system字体带来的空隙。如下:

.container{
	width: 600px;
	height: 60px;
	margin: 0 auto;
	font-size: 0;
	font-family: -apple-system;
	letter-spacing: -4px;
}
span{
	display: inline-block;
	width: 60px;
	height: 60px;
	background: #333;
	color: #fff;
	font-size: 24px;
	letter-spacing: 0;
}

从html结构上看,保持了优雅,但是css上觉得怪怪的,letter-spacing是默认继承属性,导致容器和行内块子元素都要声明一个letter-spacing。

{{:blog:2016:04:4.png?200|}}

方法三

方法二看似很巧妙地解决了在safari下的问题。不过,抱着怀疑的精神来验证这个方案的完美性,验证的标准很简单 -- -- 无缝。

将行内块的背景做成半透明如下:

.container{
	width: 600px;
	height: 60px;
	margin: 0 auto;
	font-size: 0;
	font-family: -apple-system;
	letter-spacing: -4px;
}
span{
	display: inline-block;
	width: 60px;
	height: 60px;
	background: rgba(0,0,0,3);
	color: #fff;
	font-size: 24px;
	letter-spacing: 0;
}

在PC的safari和ip6的safari的观察结果如下:

{{:blog:2016:04:5.png?200|}}

{{:blog:2016:04:6.jpg?200|}}

很不幸地。。。。letter-spacing并不能完美解决无缝平铺的问题,边缘出现叠加了!!!

可以取巧地在container把font-family的字体改成none,然后再在行内块元素把font-family重置为 -apple-system,如下:

.container{
	width: 600px;
	height: 60px;
	margin: 0 auto;
	font-size: 0;
	font-family: Helvetica;
}
span{
	display: inline-block;
	width: 60px;
	height: 60px;
	background: rgba(0,0,0,3);
	color: #fff;
	font-size: 24px;
	font-family: -apple-system;
}

{{:blog:2016:04:7.png?200|}}

效果上是可以完美解决,html结构上也可以保持优雅。不过css上看跟letter-spacing方案相近。

后记

本文记于:2016/04/06 17:10
**当前(2017/07/11)主流的safari已经不存在这个问题了。这个问题只存在于 ios8.x **

解决 Android 浏览器下 line-height 垂直居中偏离问题

移动端网页开发中,经常会涉及到一些小按钮或者小标签,比如这种:

对于一般 PC 浏览器以及 iOS 设备的浏览器表现就是我们想要的居中效果,但是大部分 Android 设备的浏览器文字都会稍微向上偏离,如下图所示:

测试表明,字体字号为奇数的两倍时(比如 10px、14px、18px、22px、26px),会出现严重偏移现象。

其实系统之间效果的差异跟我们的字体类型、系统排版引擎、浏览器都有关系,其实不影响用户浏览与操作等体验,我们可以忽略这些问题,对于一些居于使用场景偏离的比较明显的,可以使用下面提到的两种处理方案。

方案一:

我们可以通过 transform: scale 来处理,比如,字体大小是 8px,我们把字体设定为 16px,然后通过 scale(0.5) 缩放至一倍大小,简单粗暴。

注意:放大两倍会使得容器被撑开占位。

方案二:

结合行高、对齐的关系,结合伪元素得出的黑科技,亲测效果很理想。

.jd::before {
    content: '';
    display: inline-block;
    vertical-align: middle;
    width: 0;
    height: 100%;
    margin-top: 1px;
}

弹幕动效在 iOS 在有时不会动的BUG

年货 SNS 在做弹幕的时候发现,在ip6下,弹幕有时候会不做去动画(animation),如下:

图示

概率大概是 50%,前一步测试会发现 mac 下的 safari 也会有这个BUG。其实这个BUG由来已久了,只是一直没遇到过,所以有点好奇它出现的原因。

BUG定位

猜想1

弹幕是用 js 动态生成并插入页面的,如下:

图示

所以,我猜想:是不是由于 js 在页面加载未完成前插入 DOM 节点造成的BUG。于是把js插入的代码直接插到 DOM上:

图示

测试发现ip6明显好转但却仍然会有一点点的BUG:

图示

但是却意外发现 mac 下的 safari 情况在变糟糕:弹幕动效失效的概率超过7成。也就是当前的猜想是错的。

猜想2

弹幕动画是在页面载入时就有了,而页面在载入的时候比较耗性能,于是我猜想:页面载入完成后再使用弹幕动画可以解决BUG

页面载入有两个事件:window.load 与 DOMContentLoaded,对两个都进行测试:

DOMContentLoaded:
"interactive" == document.readyState ? showBullet(bulletData) : document.addEventListener("DOMContentLoaded", function() {
          showBullet(bulletData); // 显示弹幕
     });

window.load:
"complete" == document.readyState ? showBullet(bulletData) : window.addEventListener("load", function() {
          showBullet(bulletData); // 显示弹幕
     });

测试的结果是使用 DOMContentLoaded 不能解决弹幕BUG,而 window.onload 可以完美解决弹幕的BUG。也不是说猜想2是靠谱的!

BUG的解决方案

在确保页面加载完成后再开启弹幕动画:

window.load:
"complete" == document.readyState ? showBullet(bulletData) : window.addEventListener("load", function() {
          showBullet(bulletData); // 显示弹幕
     });

快速解决margin折叠问题

  1. 什么情况才会出现:2个或多个毗邻的的普通流中的块元素垂直方向上的 margin 会折叠

    • 毗邻 : 是指没有被非空内容、padding、border 或 clear 分隔开
    • 垂直方向: 是指只有垂直方向的 margin 才会
  2. 如何解决margin叠加问题?

    • 浮动元素、inline-block 元素、绝对定位元素的 margin 不会和垂直方向上其他元素的 margin 折叠
    • 创建了BFC的元素和它的子元素不会发生折叠
    • 触发BFC的因素是float(除了none)、overflow(除了visible)、display(table-cell/table-caption/inline-block)、position(除了static/relative)
  3. 日常开发出现情况
    有些时候会出现上面一些感觉比较诡异的事情,子元素明明设置了margin值,但是却没有撑开父元素,此时只要给父元素设置overflow:hidden;或者触发BFC即可。

看图更容易理解:

  1. 上下元素margin值重叠
    wx20170705-150418 2x

  2. 父子元素margin值重叠

22

使用 exif.js 和 html2canvas 遇到的问题

使用到的库

将拍摄的图像绘制到 Canvas 后,图像方向不对

在微信中使用 <input type="file" multiple accept="image/*"> 传入照片后,绘制到 Canvas 中时会发现绘制的图像方向不对(手 Q 端貌似不会存在这个问题),这时需要使用 exif.js 来解决。

Exif.js 提供了 JavaScript 读取图像的原始数据的功能扩展,例如:拍照方向、相机设备型号、拍摄时间、ISO 感光度、GPS 地理位置等数据。

<input class="uploadBtn" type="file" multiple accept="image/*">
document.querySelector(".uploadBtn").addEventListener("change", previewImgFile, false);

function previewImgFile() {

    var _files = files || event.target.files;
    var _index = index || 0;
    var reader = new FileReader();

    reader.onload = function(event) {

        var image = new Image();
            image.src = event.target.result;

        var orientation;

        image.onload = function() {

            EXIF.getData(image, function() { // 获取图像的数据

                EXIF.getAllTags(this); // 获取图像的全部数据,值以对象的方式返回
                orientation = EXIF.getTag(this, "Orientation"); // 获取图像的拍摄方向

                var rotateCanvas = document.createElement("canvas"),
                    rotateCtx = rotateCanvas.getContext("2d");

                // 针对图像方向进行处理
                switch (orientation) {

                    case 1 :
                        rotateCanvas.width = image.width;
                        rotateCanvas.height = image.height;
                        rotateCtx.drawImage(image, 0, 0, image.width, image.height);
                        break;
                    case 6 : // 顺时针 90 度
                        rotateCanvas.width = image.height;
                        rotateCanvas.height = image.width;
                        rotateCtx.translate(0, 0);
                        rotateCtx.rotate(90 * Math.PI / 180);
                        rotateCtx.drawImage(image, 0, -image.height, image.width, image.height);
                        break;
                    case 8 :
                        rotateCanvas.width = image.height;
                        rotateCanvas.height = image.width;
                        rotateCtx.translate(0, 0);
                        rotateCtx.rotate(-90 * Math.PI / 180);
                        rotateCtx.drawImage(image, -image.width, 0, image.width, image.height);
                        break;
                    case 3 : // 180 度
                        rotateCanvas.width = image.width;
                        rotateCanvas.height = image.height;
                        rotateCtx.translate(0, 0);
                        rotateCtx.rotate(Math.PI);
                        rotateCtx.drawImage(image, -image.width, -image.height, image.width, image.height);
                        break;
                    default :
                        rotateCanvas.width = image.width;
                        rotateCanvas.height = image.height;
                        rotateCtx.drawImage(image, 0, 0, image.width, image.height);

                }

                var rotateBase64 = rotateCanvas.toDataURL("image/jpeg", 0.5);

            });

        }

    }

    reader.readAsDataURL(_files[_index]);

}

html2canvas 使用过程中遇到的问题

1、图片模糊

默认生成的 Canvas 是 1 倍图,在移动端 Retina 屏幕下显示模糊,可以通过 2 倍图的方式解决:

var stageWidth = $(".stage").width(),
    stageHeight = $(".stage").height();

var retinaCanvas = document.createElement("canvas");
retinaCanvas.width = stageWidth * 2;
retinaCanvas.height = stageHeight * 2;
retinaCanvas.style.width = stageWidth + "px";
retinaCanvas.style.height = stageHeight + "px";

var ctx = retinaCanvas.getContext("2d");
ctx.scale(2, 2);

html2canvas(document.querySelector(".stage"), {
    canvas: retinaCanvas,
    onrendered: function(canvas) {
        var base64 = canvas.toDataURL(); // 返回 base64
    }
});

2、图片偏移

html2canvas 是解析 DOM 元素实际的样式来生成图片,如果对元素位置进行调整,例如设置了 topleft 或者 margin-topmargin-left 的样式会导致生成的图片偏移。

ctx.transform(-x, -y); // 计算一下偏移的值,通过 transform 方法来修正回去

3、ICON 模糊

即使是使用了 2 倍图,生成后的图片里的 ICON 还是模糊,可以用 SVG 的方式去代替图片 ICON。

4、不支持设置 border-radius 的元素

如果你需要在一个头像中设置 border-radius: 50% 在转为图片后,你会发现并不成功,解决方法在源码 html2canvas.js 中加入以下代码:

tlh = borderRadius[0][0],
tlv = borderRadius[0][1],
trh = borderRadius[1][0],
trv = borderRadius[1][1],
brh = borderRadius[2][0],
brv = borderRadius[2][1],
blh = borderRadius[3][0],
blv = borderRadius[3][1];

/* S 插入这段代码 */
var halfHeight = Math.floor(height / 2);

tlh = tlh > halfHeight ? halfHeight : tlh;
tlv = tlv > halfHeight ? halfHeight : tlv;
trh = trh > halfHeight ? halfHeight : trh;
trv = trv > halfHeight ? halfHeight : trv;
brh = brh > halfHeight ? halfHeight : brh;
brv = brv > halfHeight ? halfHeight : brv;
blh = blh > halfHeight ? halfHeight : blh;
blv = blv > halfHeight ? halfHeight : blv;
/* E 插入这段代码 */

var topWidth = width - trh,
    rightHeight = height - brv,
    bottomWidth = width - brh,
    leftHeight = height - blv;

反正切函数 Math.atan() 与 Math.atan2() 的区别

我们可以使用正切Math.tan()操作将角度转变为斜率,那么怎样利用斜率来转换为角度呢?可以利用斜率的反正切函数将它转换为相应的角度。Math.atan()Math.atan2()两个函数可以计算反正切,接下来分析一下具体用法:

一、Math.atan()

Math.atan()接受一个参数:用法如下:

  angel = Math.atan(slope)  //slope值计算为y/x (斜率比值无法判断 y、x方向,如-1/-1, 1/-1等情况)
  //angel为一个角度的弧度值,`slope`为直线的斜率,是一个数字,这个数字可以是负的无穷大到正无穷大之间的任何一个值(tan的取值范围)

不过,利用它进行计算比较复杂。因为它的周期性,一个数字的反正切值不止一个。例如atan(-1)的值可能是45度,也可能是225度。这样就是它的周期性,对于正切函数来说,它的周期是180度,所以两个相差180度的角具有相同的正切和斜率:

tanθ=tan(θ+180) 

然而,Math.atan()只能返回一个角度值,因此确定它的角度非常的复杂,而且,90度和270度的正切是无穷大,因为除数为零,我们也是比较难以处理的,因此我们更多的会采用第二个函数Math.atan2()进行处理。

二、Math.atan2()

Math.atan2()接受两个参数x和y,方法如下:

angel=Math.atan2(y,x) 

// x 指定点的 x 坐标的数字。 
// y 指定点的 y 坐标的数字。 

计算出来的结果angel是一个弧度值,也可以表示相对直角三角形对角的角,其中 x 是临边边长,而 y 是对边边长。

三、 对比Math.atan() 与 Math.atan2()输出测试

  //输入弧度值,return 角度值
  function trace(x){
    //弧度=角度*Math.PI/180
    return 180*x/Math.PI
  }

  测试如下:
  ①对比一
  x=Math.atan(1)//计算正切值为1的数字对应的弧度值,输出弧度值0.785398163397448 (当斜率值为-7/-7或7/7)
  trace(x) //输出45 

  x=Math.atan2(7,7) //输出弧度值 0.785398163397448  

  trace(x)   //输出45 

  ②对比二
  x=Math.atan2(7,-7) //输出弧度值 2.35619449019234 
  trace(x)   //转换为角度值 135  

  x=Math.atan2(-7,7) //输出弧度值 -0.785398163397448
  trace(x)  //转换为角度值输出-45

  x=Math.atan2(-7,-7)   //输出弧度值 -2.35619449019234
  trace(x)  //转换为角度值输出-135

从这些测试可以看出,通过坐标系的自动调整,可以很自由的计算出处于不同象限的位置相对应的角度.

四、 计算两点间连线的倾斜角

Math.atan2()函数返回点(x,y)和原点(0,0)之间直线的倾斜角,那么如何计算任意两点间直线的倾斜角呢?

其实只需要将两点x、y坐标分别相减得到一个新的点(x2-x1,y2-y1),然后利用它求出角度就可以了。使用下面的一个转换可以实现计算出两点间连线的夹角:

  //输入弧度值,return 角度值
  function trace(x){
    //弧度=角度*Math.PI/180
    return 180*x/Math.PI
  }
  var x = Math.atan2(y2-y1,x2-x1);  //得到弧度值
  trace(x)  //获取角度值

具体实例例子:

  //测试,计算点(3,3)和(5,5)构成的连线的夹角 
  x=Math.atan2(5-3,5-3) //输出弧度值 0.785398163397448
  trace(x)  //输出角度值 45 

参考资料:

JavaScript atan() 方法

JavaScript atan2() 方法

使用 not 简化样式

常规我们写一个导航,每个导航间都有空隙(除了最后一个),以下:

<div class="nav">
    <a href="#">首页</a>
    <a href="#">购物圈</a>
    <a href="#">个人中心</a>
</div>
.nav a {
    margin-right: 10px;
}
.nav a:last-child {
    margin-right: 0;
}

这一点都不优雅!使用 not 来简化样式

.nav a:not(:last-child) { /* 除了最后一个 */
    margin-right: 10px;
}

常用商品组占位图实现

常见商品组占位图情况:

.cover {
    position: relative;
    height: 0;
    overflow: hidden;
    padding-top: 100%;
    img {
        display: block;
        width: 100%;
        height: auto;
        position: absolute;
        top: 0;
        left: 0;
    }
}

如果是其他比例的商品图与容器比例不符,可以这样处理:

.cover {
    position: relative;
    height: 0;
    overflow: hidden;
    padding-top: 100%;
    img {
        display: block;
        position: absolute;
        top: 50%;
        left: 50%;
        max-width: 100%;
        max-height: 100%;
        transform: translate(-50%,-50%);
    }
}

如果商品图需要完全铺满,可以进一步这样处理:

需要配合脚本判断图片宽高,宽大于高时使用类名 .full_h,高大于宽时使用类名 .full_w

.cover {
    position: relative;
    height: 0;
    overflow: hidden;
    padding-top: 100%;
    img {
        display: block;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%,-50%);
        &.full_w {width: 100%;}
        &.full_h {height: 100%;}
    }
}

抛开兼容问题,我们还有一个更适合的属性 object-fit,可以这样处理:

.cover {
    position: relative;
    height: 0;
    overflow: hidden;
    padding-top: 100%;
    img {
        display: block;
        position: absolute;
        top: 0;
        left: 0;
        object-fit: contain 或 cover;
    }
}

object-fit 兼容表:

Git 忽略规则及 .gitignore 规则不生效的解决办法

在 Git 中如果想忽略掉某个文件,不让这个文件提交到版本库中,可以使用修改根目录中 .gitignore 文件的方法(如无,则需自己手工建立此文件)。这个文件每一行保存了一个匹配的规则例如:

# 此为注释 – 将被 Git 忽略
*.a       # 忽略所有 .a 结尾的文件
!lib.a    # 但 lib.a 除外
/TODO     # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/    # 忽略 build/ 目录下的所有文件
doc/*.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt

规则很简单,不做过多解释,但是有时候在项目开发过程中,突然心血来潮想把某些目录或文件加入忽略规则,按照上述方法定义后发现并未生效,原因是 .gitignore 只能忽略那些原来没有被 track 的文件,如果某些文件已经被纳入了版本管理中,则修改 .gitignore 是无效的。那么解决方法就是先把本地缓存删除(改变成未 track 状态),然后再提交:

git rm -r --cached .
git add .
git commit -m 'update .gitignore'

转自 梧桐树下 » Git忽略规则及.gitignore规则不生效的解决办法

fixed在移动端的坑

  1. fixed在某些情况下可能导致容器内的子元素的1px边框线消失,即使使用z-index也无法解决。
    解决方法:可以使用translateZ属性来解决

  2. fixed定位的容器内不能带有input,这是常见的bug。
    解决方法: 在input聚焦的时候去掉fixed定位状态,改为absolute。

  3. fixed+可滚动的容器内会导致fixed定位的子元素在滚动时定位失效,滚动完成后才正常回到fixed的位置。
    解决方法:尽量不要在可滚动的容器内包含fixed定位的子元素。

可以使用css实现的多种字体效果集合

为了减少翻资料的时间成本,在这里整理一下日常中需要处理的各种特殊字体。

  1. 文字发光
text-shadow: 0 0 0, 0 0 2px;

33

  1. 渐变文字
background: -webkit-gradient(linear, left top, right top, from(#890ffa), to(#d80078));
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;

2342

  1. 立体文字
color: #ffe799;
text-shadow: 0 1px 0px #987400;

1241421

  1. 文字描边
text-shadow: #000 2px 0 0, #000 0 2px 0, #000 -1px 0 0, #000 -1px -1px 0;

1515

可以自己调整里面的属性,实现相同的效果~

解析HTML/XML生成文档对象模型

前段时间研究svg压缩处理优化,针对文本html、xml文件解析成DOM文档对象,一直困扰着我。那么,今天讲讲借助htmlparser2sax-jsxmldom 解析HTML/XML。

htmlparser2

htmlparser2可以用来处理HTML / XML / RSS的解析器,可以接收流文件,并提供回调接口。cheerio底层就是用此原理。

安装使用

htmlparser2线上解析演示

npm install htmlparser2

使用todo

//解析
var htmlparser = require("htmlparser2");
var parser = new htmlparser.Parser({
    onopentag: function(name, attribs){
        if(name === "script" && attribs.type === "text/javascript"){
            console.log("JS! Hooray!");
        }
    },
    ontext: function(text){
        console.log("-->", text);
    },
    onclosetag: function(tagname){
        if(tagname === "script"){
            console.log("That's it?!");
        }
    }
}, {decodeEntities: true});
parser.write("Xyz <script type='text/javascript'>var foo = '<<bar>>';</ script>");
parser.end();

//生成简单的文档对象
var htmlparser = require("htmlparser2");
var rawHtml = "Xyz <script language= javascript>var foo = '<<bar>>';< /  script><!--<!-- Waah! -- -->";
var handler = new htmlparser.DomHandler(function (error, dom) {
    if (error){
    	throw new error()
    }else{
    	console.log(dom);
    }
       
});
var parser = new htmlparser.Parser(handler);
parser.write(rawHtml);
parser.done();

htmlparser2监听事件Events

监听处理键值对象函数,仅对有效的键值进行处理,否则中断。

  • onopentag( name, attributes)
  • onopentagname( name)
  • onattribute( name, value)
  • ontext( text)
  • onclosetag( name)
  • onprocessinginstruction( name, data)
  • oncomment( data)
  • oncommentend()
  • oncdatastart()
  • oncdataend()
  • onerror( error)
  • onreset()
  • onend()

htmlparser2解析方法

  • write (别名: parseChunk)

解析数据块,触发相应的回调函数

  • end (别名: done)

解析buffer数据和清除堆栈结束,触发 onend 回调函数。

  • reset

重置buffer以及stack,触发 onreset 函数

  • parseComplete

重置解析器解析数据,触发调用 end。

htmlparser2参数 Option

  • Option: xmlMode

表示是否是特殊标签<script><style>应该得到特殊处理, 如果是空"empty" 的标签(如< br >)含有子元素。如果没有特殊处理,则特殊标签将做文本处理。
解析其它XML内容(不包含HTML文件), 设置为true。默认值:false。

  • Option: decodeEntities

设置为true,文档中内容实体部分将解码。默认值为false。

  • Option: lowerCaseTags

设置为true,所有标签将小写展示。xmlMode不设置情况下,默认值为true。

  • Option: lowerCaseAttributeNames

设置为true,所以属性name将设置为小写。由于影响解析速度,默认值为false。

  • Option: recognizeCDATA

设置为true,CDATA区域将被认为文本,即使xmlMode选项不启用。注意,xmlMode被设置为true,那么CDATA节总是会被认为是文本。

  • Option: recognizeSelfClosing

设置为true,其关闭标签将触发 onclosetag 事件即使xmlMode没有设置为true。注意: xmlMode设置为true,那么自闭标签总是被认可。

htmlparser2主要是提供了对HTML / XML / RSS的解析,效率比较高,而相应生成简单的文档对象,需要借助domhandler模块,只是增加了DOM level 1不便于操作。

sax-js

sax-style风格处理XML和HTML的解析器,sax js

安装使用

npm install sax-js

使用todo

var sax = require("./lib/sax"),
  strict = true, // set to false for html-mode
  parser = sax.parser(strict);

parser.onerror = function (e) {
  // an error happened.
};
parser.ontext = function (t) {
  // got some text.  t is the string of text.
};
parser.onopentag = function (node) {
  // opened a tag.  node has "name" and "attributes"
};
parser.onattribute = function (attr) {
  // an attribute.  attr has "name" and "value"
};
parser.onend = function () {
  // parser stream is done, and ready to have more stuff written to it.
};

parser.write('<xml>Hello, <who name="world">world</who>!</xml>').close();

// stream usage
// takes the same options as the parser
var saxStream = require("sax").createStream(strict, options)
saxStream.on("error", function (e) {
  // unhandled errors will throw, since this is a proper node
  // event emitter.
  console.error("error!", e)
  // clear the error
  this._parser.error = null
  this._parser.resume()
})
saxStream.on("opentag", function (node) {
  // same object as above
})
// pipe is supported, and it's readable/writable
// same chunks coming in also go out.
fs.createReadStream("file.xml")
  .pipe(saxStream)
  .pipe(fs.createWriteStream("file-copy.xml"))

由于sax-js解析的许多方法基本与htmlparser2一致,只是解析效率上比htmlparser2稍差。

XMLDOM

XMLDOM是一款非常强大的解析工具,可实现浏览使用的 javascript文档对象模型 (W3C DOM) 。完美兼容DOM Level 2 以及部分 DOM Level 3。支持支持DOMParser和XMLSerializer接口浏览。解析为文档对象

参考资料:
创建和导出SVG的技巧
SVG 导出与优化

安装:

npm install xmldom

使用todo

var DOMParser = require('xmldom').DOMParser;
var doc = new DOMParser().parseFromString(
    '<xml xmlns="a" xmlns:c="./lite">\n'+
        '\t<child>test</child>\n'+
        '\t<child></child>\n'+
        '\t<child/>\n'+
    '</xml>'
    ,'text/xml');
//doc相当于document,doc.documentElement相当于document.documentElement
doc.documentElement.setAttribute('x','y');
doc.documentElement.setAttributeNS('./lite','c:x','y2');
var nsAttr = doc.documentElement.getAttributeNS('./lite','x')
console.info(nsAttr)
console.info(doc)

API特征

DOMParser

parseFromString(xmlsource,mimeType)

options extension by xmldom(not BOM standard!!)

//added the options argument
new DOMParser(options)

//errorHandler is supported
new DOMParser({
    /**
     * locator is always need for error position info
     */
    locator:{},
    /**
     * you can override the errorHandler for xml parser
     * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
     */
    errorHandler:{warning:function(w){console.warn(w)},error:callback,fatalError:callback}
    //only callback model
    //errorHandler:function(level,msg){console.log(level,msg)}
})

XMLSerializer 可以将DOM subtree 和 DOM document转换为文本

serializeToString(node)

DOM level2 方法(method) 和 属性(attribute):

attribute:
    nodeValue|prefix
readonly attribute:
    nodeName|nodeType|parentNode|childNodes|firstChild|lastChild|previousSibling|nextSibling|attributes|ownerDocument|namespaceURI|localName
method: 
    insertBefore(newChild, refChild)
    replaceChild(newChild, oldChild)
    removeChild(oldChild)
    appendChild(newChild)
    hasChildNodes()
    cloneNode(deep)
    normalize()
    isSupported(feature, version)
    hasAttributes()
method:
    hasFeature(feature, version)
    createDocumentType(qualifiedName, publicId, systemId)
    createDocument(namespaceURI, qualifiedName, doctype)

参考资料:

domhandler

DOMParser

XMLSerializer 可以将DOM subtree 和 DOM document转换为文本

微信下 iOS Android 全屏视频

在制作全屏视频需求时,你会发现 iOS、Android 在这一块多多少少都存在一些问题。

iOS

iOS 通过 webkit-playsinlineplaysinline 实现内嵌全屏,不调用系统全屏,有必要的情况下配合 iphone-inline-video 库使用。

<video src="video.mp4" webkit-playsinline playsinline></video>
/* 隐藏 iOS 视频的播放暂停按钮 */
video::-webkit-media-controls-play-button,
video::-webkit-media-controls-start-playback-button {
    opacity: 0;
    pointer-events: none;
    width: 5px;
}

Android

Android 除非你的域名下微信的白名单,否则你会面临两种选择:

1、使用 X5 自建播放器
2、隐藏导航条的全屏视频

自建播放器就跟废的没两样,全屏偶尔还能用上,通过 x5-video-player-type="h5"x5-video-player-fullscreen="true"可视频全屏,同时顶部的微信标题条也会消失,视频内会有一个全屏的 icon 按钮,退出全屏会使视频暂停

<video src="video.mp4" x5-video-player-type="h5" x5-video-player-fullscreen="true"></video>

Safari 下 Date 转换日期输出 NaN

最近在一次需要计算关注天数时,发现在 Safari 浏览器使用 new Date() 时出现 NAN 的情况

接口返回的时间格式数据是:"2017-06-11 13:50:30"

let time = '2017-06-11 13:50:30';

Date.parse(new Date(time)); // Safari 下返回 NaN

Date.parse(new Date(time)); // Chrome 下正常返回 1507687810000

问题就出在 Safari 对于这个格式 YYYY-MM-DD HH:MM:SS 无法解析,所以我们需要做的是将其转化为 YYYY/MM/DD HH:MM:SS

let time = '2017-06-11 13:50:30';

Date.parse(new Date(time.replace(/-/g, '/'))); // Safari 下正常返回 1507687810000

隐藏滚动条

.example::-webkit-scrollbar {
    width: 0;
    height:0;
    display: none;
}

但是!

该方法在 iOS 版微信升级到 WKWebView 后有很大的概率无法隐藏滚动条,在旧的 UIWebView 中不存在此问题。

iOS 微信 6.5.3 版本开始支持手动切换 WKWebView 和 UIWebView 的办法:

点击微信会话列表页点击右上角 + 号按钮,选择菜单中的 添加朋友,在添加朋友界面的搜索框中输入字符串::switchweb,再点击键盘右下角搜索按钮。切换成功后会提示当前使用的内核是 UIWebview 或是 WKWebview。

校验切换方法:

通过命令成功切换到 WKWebview 后,可通过以下方法验证当前网页使用的是否是 WKWebview 内核。
微信内任意入口进入任意网页,在网页加载成功后向下拉动页面(或点击网页右上角菜单按钮),使之显示出地址栏,当地址栏以 此网页由 开头即为当前使用WKWebview,若以 网页由 则是使用的UIWebview。

SVG 文本路径居中

路径文本

svg 中可以控制文本沿指定路径显示,只需要 标签就可以完成。以下是一个实例:

<svg viewBox="0,0,332,48">
  <path id="textPath" d="m0.749997,39.860256c92.99817,20.99958 250.99507,-15.99969 327.99356,1.99996" opacity="0.5" stroke-opacity="null" stroke-width="1" stroke=“#000" fill="none"/>
  <text fill="#fff" font-size="28" x="0" y="0">
    <textPath xlink:href="#textPath">
      利益点利益点利益点
    </textPath>
  </text>
</svg>

路径文本居中

有时候,我们需要将文本沿着svg中心点居中。 标签的属性如下:

属性名称 属性说明
x 起始点 x 轴的座标
y 起始点 y 轴的座标
dx 座标的 x 轴偏移位置
dy 座标的 y 轴偏移位置
rotate 旋转偏移的角度
textLength 元素的长度,这会影响输出的宽度
transform 文字的变形效果,类似 css 中的 transform

可以使用 x,dx 来定位中心点。但是,却会发现这样做不是文本居中,而是文本从中间开始:

<svg viewBox="0,0,332,48">
  <path id="textPath" d="m0.749997,39.860256c92.99817,20.99958 250.99507,-15.99969 327.99356,1.99996" opacity="0.5" stroke-opacity="null" stroke-width="1" stroke=“#000" fill="none"/>
  <text fill="#fff" font-size="28" x=“50%" y="0">
    <textPath xlink:href="#textPath">
      利益点利益点利益点
    </textPath>
  </text>
</svg>

查阅了一下资料,在这篇文章 http://blog.csdn.net/salmonellavaccine/article/details/43041981 ,看到可以使用 的 text-anchor 和 startOffset 来实现居中的效果。

text-anchor: 文本相对于起始点的对齐方式。使用 middle 是居中。
startOffset: 离起始点的相对位置。

<svg viewBox="0,0,332,48">
  <path id="textPath" d="m0.749997,39.860256c92.99817,20.99958 250.99507,-15.99969 327.99356,1.99996" opacity="0.5" stroke-opacity="null" stroke-width="1" stroke=“#000" fill="none"/>
  <text fill="#fff" font-size="28">
    <textPath xlink:href=“#textPath"  text-anchor="middle" startOffset="50%">
      利益点利益点利益点
    </textPath>
  </text>
</svg>

一个有意思的地方:text-anchor 也是 的属性。也就是说, 使用 text-anchor="middle” 和 dx/x = “50%” 也可以实现居中。

Sass/Compass中引入全局库

有一些通用的样式和函数方法,我们在每个项目里都会用到,但麻烦的是每个项目都要单独把相关的库文件引用进项目,非常不方便,而且文件也比较冗余。终于发现了引用全局变量的方法。

compass watch --import-path D://sass//lib

compass命令带 –import-path 即可添加全局引用库。

sass --update --load-path D://sass//lib

sass命令带 –load-path 即可添加全局引用库。 如果你修改随意修改库文件,可能会对之前的项目造成影响,修改全局库文件时应当谨慎,最好在单独的项目类,重写需要修改的方法。

层叠式上下文

参考:张鑫旭的《深入理解 CSS 中的层叠上下文和层叠顺序》

stack

层叠顺序

元素发生层叠时,一套垂直显示顺序规则。

同一个层叠上下文中:

	正 z-index
	非 static 定位元素、不依赖 z-index 的层叠上下文元素
	inline、inline-block 行内盒
	float 盒子
	block 块盒
	负 z-index
	层叠上下文的 background、border

层叠水平

每个元素都有。

层叠上下文元素的层叠水平由其 z-index 确定,相同则后者层叠水平更高。

层叠水平的比较只有在当前层叠上下文元素中才有意义。

当层叠水平(level)相同、层叠顺序(order)也相同时,DOM 文本流中处于后面的元素覆盖处于前面的元素。

总的来说,

同一个层叠上下文,看层叠顺序,层叠顺序一样,则后来者位于上。

不同的层叠上下文,看所在的层叠上下文元素的层叠顺序与层叠水平。

如何创建层叠上下文:

  1. <html>

  2. z-index 为数值(包括0)的非 fixed 定位元素

  3. 其他

CSS3 对层叠上下文的影响

  1. 父元素 display: flex;display: inline-flex;,同时子元素(必须为直系子元素)的 z-index 不为auto。这时,这个子元素成为层叠上下文元素。

  2. opacity 的值不为1的元素会自动成为层叠上下文元素。

  3. 带有 transform 属性的元素会自动成为层叠上下文元素。

  4. 带有 filter 属性的元素会自动成为层叠上下文元素。

安卓系统下,fixed 元素内部子元素使用组件 %border-btm-1pt,1像素线会被父元素的背景色盖住。

问题在于 transform-origin:50% 100%; 垂直方向的缩放原点100%。

解决办法,自身加 z-index,只要大于等于0的数值就可以。也可以对父元素加 z-index 创建层叠式上下文。

CSS3 滤镜

.example-1 {
    -webkit-filter: blur(5px); /* 毛玻璃 */
}

.example-2 {
    -webkit-filter: grayscale(0.5); /* 灰度 */
}

.example-3 {
    -webkit-filter: sepia(0.5); /* 褐色 */
}

.example-4 {
    -webkit-filter: brightness(3); /* 亮度 */
}

.example-5 {
    -webkit-filter: hue-rotate(180deg); /* 色相 */
}

.example-6 {
    -webkit-filter: invert(1); /* 反色 */
}

.example-7 {
    -webkit-filter: opacity(0.5); /* 透明度 */
}

.example-8 {
    -webkit-filter: saturate(5); /* 饱和度 */
}

.example-9 {
    -webkit-filter: contrast(0.5); /* 对比度 */
}

.example-10 {
    -webkit-filter: drop-shadow(10px 10px 5px rgba(0, 0, 0, 0.9)); /* 阴影 */
}

DEMO戳这里

视频、音频播放和APP、M版的分享遇到的一些坑

最近在做涉及视频、音频播放和APP、M版的分享的相关活动中,遇到了一些比较坑的问题。
分享出来,让大家一起了解一下。

视频音频播放的坑:

1、在微信大入口之外的微信环境(分享给好友的链接或朋友圈的链接)可以自动播放视频和音频 ( .play 能触发)。

2、在微信大入口之内,必须要等到微信的 JSBridge Ready 了才能触发 .play,否则不会自动执行。

document.addEventListener("WeixinJSBridgeReady", function() {  
   $("#video")[0].play();  
});  

最好监听了之后,在当前作用域内,立即执行。而不是使用 setTimeout 等计时器,延时执行。这样可能会导致在IOS下偶尔不能触发 .play(经测试,大概10%~20%的几率不能触发)。

3、在大入口内,且 JSBridge Ready 了,如果使用摇一摇,也无法触发 .play(经测试,.play 的触发不能直接在监听了 devicemotion 的事件处理内)。需要在摇一摇之前预先加载 .load

document.addEventListener("WeixinJSBridgeReady", function() {  
    $("#video")[0].load();  
    _shake.start({  
        duration: 500,  
        onShaking: function() { //正在摇  
        },  
        onEnd: function() { //一次摇一摇结束  
            $("#video")[0].play();  
        }  
    });  
});  

4、微信大入口在没有使用 JSBridge,而是通过点击来播放的点击事件,只能是 click 事情,不是
touchstart 事件

//正确的做法

$("#play").on("click",function(){  
    $("#video")[0].play();  
});  

//不正确的做法

$("#play").on("touchstart",function(){  
    $("#video")[0].play();  
}); 

APP和M版本分享的坑

  1. 分享文案不能使用的一些字符:现金 红包 #
  2. M版本的分享没有回调

以上总结内容转自 @少露

判断是否为 WebP、APNG 格式

WebP

(function() {
    var img = new Image();
    img.onload = function() {
        if (img.width > 0 && img.height > 0) {
            document.documentElement.className = "webp";
            window.webpSupport = true;
        }
    }
    img.onerror = function() {}
    img.src = "data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA";
}());

APNG

(function() {
    "use strict";
    var apngTest = new Image(),
    ctx = document.createElement("canvas").getContext("2d");
    apngTest.onload = function () {
        ctx.drawImage(apngTest, 0, 0);
        self.apng_supported = ctx.getImageData(0, 0, 1, 1).data[3] === 0;
    };
    apngTest.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACGFjVEwAAAABAAAAAcMq2TYAAAANSURBVAiZY2BgYPgPAAEEAQB9ssjfAAAAGmZjVEwAAAAAAAAAAQAAAAEAAAAAAAAAAAD6A+gBAbNU+2sAAAARZmRBVAAAAAEImWNgYGBgAAAABQAB6MzFdgAAAABJRU5ErkJggg==";
}());

border-image使用过程中遇到的几个问题

这次年货,为了增添气氛,很多地方都用了边框,由于边框高度的不固定,给构建着实带了不小的麻烦。

通常我们处理边框的方法,无非以下两种方案:

1.切整张图片,做背景。

2.切上中下,进行拼接处理。

但是遇到,边框多种样式的时候,我可能就需要支招,切得手软,为了解决这些问题,我们尝试了border-image。

使用border-image,我们无非弄明白以下几个属性就能使用了:

border-image-source	用在边框的图片的路径。	
border-image-slice	图片边框向内偏移。	
border-image-width	图片边框的宽度。	
border-image-outset	边框图像区域超出边框的量。	
border-image-repeat	图像边框是否应平铺(repeat)、铺满(round)或拉伸(stretch)。

但是在使用过程中,可能会遇到两个这样的问题:

1.border-image使用中会产生多余的边框(手q中,在一些低端的andirod中)。

图片1

2.border-image-repeat的属性值,repeat 和 round 如何选择区分。

针对这两个问题,经过一些尝试,尝试发现:

1.border-image使用中会产生多余的边框,是由于边框图片边缘没有预留一定的空间导致的,这里建议预留1px,例图:

边框易产生多余的边框(低端机)

图片2

边框不会产生多余的边框

图片3

2.repeat 和 round 如何选择区分,round会压缩(或伸展)图片大小使其正好在区域内显示,而repeat是不管三七二十一直接重复的,而且是居中重复,多数情况建议大家可以用round,repeat会导致叠加现象,下面同种情况下实现的效果:

这个是w3cschool里面的例子,我加了round,repeat 比对:

<!DOCTYPE html>
<html>
<head>
<style> 
div
{
border:15px solid transparent;
width:300px;
padding:10px 20px;
}

#round
{
-moz-border-image:url(/i/border.png) 30 30 round;	/* Old Firefox */
-webkit-border-image:url(/i/border.png) 30 30 round;	/* Safari and Chrome */
-o-border-image:url(/i/border.png) 30 30 round;		/* Opera */
border-image:url(/i/border.png) 30 30 round;
}

#stretch
{
-moz-border-image:url(/i/border.png) 30 30 repeat;	/* Old Firefox */
-webkit-border-image:url(/i/border.png) 30 30 repeat;	/* Safari and Chrome */
-o-border-image:url(/i/border.png) 30 30 repeat;		/* Opera */
border-image:url(/i/border.png) 30 30 repeat;
}
</style>
</head>
<body>

<div id="round">在这里,图片铺满整个边框。</div>
<br>
<div id="stretch">在这里,图片被拉伸以填充该区域。</div>

<p>这是我们使用的图片:</p>
<img src="/i/border.png">

</body>
</html>

效果:

图片4
很显然,repeat出现了,叠加现象,对于我们构建来说,肯定不是特别好的,所以建议用round。

跨域图片资源权限(CORS enabled image) - HTML | MDN

HTML 规范文档为 images 引入了 crossorigin 属性, 通过设置适当的头信息 CORS , 可以从其他站点加载 img 图片, 并用在 canvas 中,就像从当前站点(current origin)直接下载的一样.

crossorigin 属性的使用细节, 请参考 CORS settings attributes.

什么是 "被污染的(tainted)" canvas?

尽管没有CORS授权也可以在 canvas 中使用图像, 但这样做就会污染(taints)画布。 只要 canvas 被污染, 就不能再从画布中提取数据, 也就是说不能再调用 toBlob(), toDataURL()getImageData() 等方法, 否则会抛出安全错误(security error).

这实际上是为了保护用户的个人信息,避免未经许可就从远程web站点加载用户的图像信息,造成隐私泄漏。

如果用户登陆过QQ等社交网站, 假若不做保护 ,则可能打开某个网站后,该网站利用 canvas 将用户的图片信息获取,上传,进而引发泄露.

示例: 从其他站点保存图片

首先, 图片服务器必须设置相应的 Access-Control-Allow-Origin 响应头。添加 img 元素的 crossOrigin 属性来请求头。比如Apache服务器,可以拷贝 HTML5 Boilerplate Apache server configs 中的配置信息, 来进行回应:

<IfModule mod_setenvif.c>
    <IfModule mod_headers.c>
    <FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
        SetEnvIf Origin ":" IS_CORS
        Header set Access-Control-Allow-Origin "*" env=IS_CORS
    </FilesMatch>
    </IfModule>
</IfModule> 

这些设置生效之后, 就可以像本站的资源一样, 保存其他站点的图片到 DOM存储) 之中(或者其他地方)。

var img = new Image,
    canvas = document.createElement("canvas"),
    ctx = canvas.getContext("2d"),
    src = "http://example.com/image"; // 具体的图片地址

img.crossOrigin = "Anonymous"; //或 img.crossOrigin = "*"; 

img.onload = function() {
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage( img, 0, 0 );
    localStorage.setItem( "savedImageData", canvas.toDataURL("image/png") );
}
img.src = src;
//  确保缓存的图片也触发 load 事件
if ( img.complete || img.complete === undefined ) {
    img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
    img.src = src;
}

参考资料:
Chrome:在WebGL中使用跨域图片
HTML规范-crossorigin属性

前端观测:正确理解rotate3d

最近,收到反馈说3D翻转在小米4下展示异常。秉承实事求是的精神,拿了部安卓机子做观测。

观测的机型:华为荣耀3C(没有小米4,华为荣耀3C与米4的异常一致)

异常截图如下:

**图一**

rotate3d的具体调用如下:

transform: rotate3d(0,0,0,90deg);

修改为:

transform: rotate3d(1,0,0,0deg);

问题得以解决:

**图二**

rotate3d(0,0,0,90deg) 与 rotate3d(1,0,0,90deg) 有什么实质的区别。w3c 相关解释:

specifies a 3D rotation by the angle specified in last parameter about the [x,y,z] direction vector described by the first three parameters. A direction vector that cannot be normalized, such as [0,0,0], will cause the rotation to not be applied.

来源:https://www.w3.org/TR/css-transforms-1/;

根据w3c的说法应该是 rotate3d(0,0,0,90deg) 会不生效。而出问题的安卓机型却把 rotate3d(0,0,0,90deg) 降级为 rotate(90deg) 或 rotateZ(90deg)了。

w3c对rotate3d的用法如下:rotate3d( <number> , <number> , <number> , <angle> )

第一次观测

为了便于说明,把上式写成 rotate3d(x,y,z,degree);

本人一开始对这个的理解是,x,y,z轴方法的旋转角度为 x * degree, y * degree ,z * degree。而下面的观测结果,打了我一巴掌,以下几个rotate3d的效果是一样的:

  1. transform: rotate3d(-1,0,0,60deg);
  2. transform: rotate3d(1,0,0,60deg);
  3. transform: rotate3d(0.1,0,0,60deg);

观测结果如下:

mac,adnroid和ios的表现都一样,只截mac上的chrome浏览的显示结果

这次观测的结论是: x,y,z的取值是:x?degree:0,y?degree:0,z?degree:0

如果这个观测结论是正确的话,很容易推断得出,一个立方体只能沿七条线进行3D翻转:

第二次观测

使用以下代码验证第一次观测的结论是否正确:

<style>
.rect{
  position: absolute;
  width: 100px;
  height: 100px;
  background-color: #333;
  left: 100px;
  top: 100px;
  -webkit-transform: rotate3d(1,1,0,30deg);/*观测关键代码*/
}
</style>
<div class="rect"></div>

观察结果如下:

**图3**

将rotate3d更改为:

-webkit-transform: rotate3d(1,.5,0,30deg);

按上次观测结论,这次观测与上次观测结果应该完全相同。而观测结果却不是,如下:

**图4**

第二次观测的结论为:x,y,z轴方法的旋转角度为 x * degree, y * degree, z * degree。(与本人最开始的理解一致)

第三次观测

第一次和第二次观测结果完全不同,但是又各有事实做证,于是我猜测:

1. rotate3d的x,y,z轴的角度值的计算为:x * degree,y * degree,z * degree;
2. 当且仅当x,y,z三个值中有两个值为0时,剩余的非0值取1;

沿用第二次观测的代码,观察下面几种情况:

  1. -webkit-transform: rotate3d(1,1,0,30deg);
  2. -webkit-transform: rotate3d(1,.4,0,30deg);
  3. -webkit-transform: rotate3d(1,1,1,30deg);
  4. -webkit-transform: rotate3d(1,4,2,30deg);

观测结果如下:

与猜测结果一致。

结论

rotate3d对x,y,z三个轴的取值遵守以下两点:

1. rotate3d的x,y,z轴的角度值的计算为:x * degree,y * degree,z * degree;

2. 当且仅当x,y,z三个值中有两个值为0时,剩余的非0值取1;

基于以上两点,rotate3d 的正确用法是:

1. 单轴翻转使用rotateX,rotateY,rotateZ
2. 使用rotate3d要避免 rotate3d(0,0,0,deg) 的情况

直接放弃rotate3d转用 rotateX,rotateY,rotateZ可以简单直接地避开所有风险点。

LayaAir 游戏引擎初体验踩坑指南

总结在使用 LayaAir 游戏引擎制作 H5 游戏中遇到的一些问题。

1、使用 HTMLDivElement 文本绘制

针对某一段文字中,某个数字需要加粗或换颜色的情况,可以使用 HTML 文本,默认情况下 htmlDivElement 的宽度为 200,文字到一定长度会自动换行,可以通过 p.style.wordWrap = false; 强制不换行或者将 width 的值设置为更大解决。

引入 laya.html.min.js

var p = new HTMLDivElement();
Laya.stage.addChild(p);
p.style.font = "-apple-system, Helvetica, sans-serif";
p.style.fontSize = 30;
p.style.wordWrap = false; // 强制不换行
p.width = 600; // 将默认 200 的宽度改为 600
p.innerHTML = "命中率" + "<span style='color: #f00'>" + 50% + "</span>" // 通过这种方式改颜色

2、绝对定位于底部

如果只需做竖屏或者横屏的页面时,我们只需要将 Laya.stage.height 舞台的高度减去元素高度即可,但是如果同时需要兼容横竖屏,导致高度不一,这样定位就稍稍有些不方便了,这时可以使用 UI 中的 Box 容器,他提供了 topbottomleftright 等属性,可以方便的定位。

引入 laya.ui.min.js

var Box = Laya.Box;
var Sprite = Laya.Sprite;

var container = new Box(); // 容器
Laya.stage.addChild(container);
container.bottom = 0; // 基于底部定位
container.left = 0;
container.width = 1334;
container.height = 30;

var sp = new Sprite();
container.addChild(sp); // 将元素添加到容器中
sp.graphics.drawRect(0, 0, 200, 30, "#ffff00");

3、drawPath 实现线条末端线帽的样式

目前自定义路径的末端样式 lineCap 在 WebGL 渲染模式下无效,只能在 Canvas 模式下才行。

var sp = new Sprite();
Laya.stage.appChild(sp);
sp.drawPath(50, 50, [
    ["moveTo", 0, 0],
    ["LineTo", 100, 0],
    ["closePath"]
], {
    fillStyle: "#00ffff"
}, {
    lineWidth: 14,
    lineCap: "round" // WebGL 模式下无效
});

4、Matter.js 旋转画布后的鼠标约束

LayaAir 集成了 Matter.js 物理引擎,Matter.js 的鼠标约束是它自身控制的,Matter.js 的显示是由 LayaAir 控制的,使用 LayaAir 旋转画布方向,但 Matter.js 不会旋转,所以会导致鼠标约束失效。解决方案是使用系统的横屏或者去修改 Matter.js 的源码,如果有约束或鼠标约束的事件,不建议旋转画布。

Laya.stage.screenMode = Stage.SCREEN_HORIZONTAL; // 自动横屏旋转画布后会导致 Matter.js 鼠标约束失效

5、使用 LayaAir 缩放后,修正 Matter.js 鼠标约束坐标

Laya.stage.on("resize", this, onResize);

function onResize() {
    // 设置鼠标的坐标缩放
	// Laya.stage.clientScaleX 代表舞台缩放
	// Laya.stage._canvasTransform 代表画布缩放
	Matter.Mouse.setScale(mouseConstraint.mouse, {
		x: 1 / (Laya.stage.clientScaleX * Laya.stage._canvasTransform.a),
		y: 1 / (Laya.stage.clientScaleY * Laya.stage._canvasTransform.d)
	});
}

6、监听动画到某一帧

监听动画到达某一帧,需要给动画使用 addLabel 方法添加标签,再监听动画指定标签即可。

var anim = new Animation();
anim.addLabel("now", 2); // 当播放到第二帧时返回 "now"  字符串
anim.play();

anim.on(Event.LABEL, this, function(ev) { // 监听 LABEL 事件
    if (ev == "now") {
        console.log("到达第二帧");
    }
});

7、改变元素层级

改变元素的层级可以使用 zOrder 属性

var sp = new Sprite();
Laya.stage.addChild(sp);
sp.zOrder = 10;

8、操作 Laya.init 生成的 Canvas

Laya.init(width, height, Laya.WebGL); // 返回 DOM 元素 Canvas

9、元素点击区域需要加 size 属性

var sp = new Sprite();
Laya.stage.addChild(sp);
sp.graphics.drawRect(10, 166, 166, 90, "#ffff00");
sp.size(166, 90); // size 的大小等于 sp 的宽高,就是鼠标的点击区域

sp.on("MOUSE_DOWN", this, function() {
    console.log("点击触发!");
});

快速理解nth-child和nth-of-type的异同

  1. nth-child是根据元素个数来计算,比如:p:nth-child(1) 是表示这是个p元素,且这是父标签的第二个孩子元素,2个条件都必须满足才会生效

  2. nth-of-type。比如:p:nth-child(3) 选中p元素的第3个元素

  3. 从下面的例子可以清晰地看到nth-child和nth-of-type的区别

   p:nth-of-type(3){color:red;}
   p:nth-child(2) {color:blue;}
   p:nth-child(3) {color:blue;}

2

商品组标签模块适配

实现商品组内标签模块的适配问题,如图效果:

HTML 结构如下:

<div class="mall_recommend">
    <div class="mall_recommend_item mall_recommend_tags">
        <div class="tags">
            <a href="#"><em>标签</em></a>
            ...
        </div>
    </div>
    <a class="mall_recommend_item" href="#">
        <div class="cover">
            <img src="http://fpoimg.com/290x290?bg_color=dddddd">
        </div>
        <div class="info">...</div>
    </a>
</div>

核心 SASS 代码:

.mall_recommend_tags {
    position: relative;
    /* 这里模拟 cover 的高度 */
    &::before {
        content: '\20';
        display: block;
        padding-top: 100%;
        height: 0;
        overflow: hidden;
    }
    /* 这里模拟 info 的高度 */
    &:after {
        content: '\20';
        display: block;
        width: 100%;
        height: 75px; /* .info 高度 */
    }
    .tags {
        position: absolute;
        top: 0;
        left: 0;
        bottom: 0;
        overflow: hidden;
        width: 100%;
        height: 100%;
        padding-bottom: 8px; /* 上下元素间隙高度总和 */
        a {
            /* 代码省略:两栏布局 */
            height: 20%; /* 高度占比 */
            margin-bottom: 2px;
	        em {
           /* 代码省略:文字水平垂直居中 */
           }
       }
   }
}

input 标签相关样式

修改 iOS 下 input disabled 颜色

input:disabled {
    -webkit-text-fill-color: #ddd;
}

设置 input 标签 placeholder 属性的样式

.example::-webkit-input-placeholder {
    color: red;
}

取消 input 默认样式

input {
    border: none;
    background: none;
    appearance: none;
    border-radius: 0;
}

取消点击 a、button、input 标签后区域半透明遮罩

a, button, input {
    -webkit-tap-highlight-color: rgba(255, 0, 0, 0);
}

关闭 iOS 默认输入法首字母大写

<input type="text" autocapitalize="off">

文字两端且均分对齐技巧

如下图几种场景,实现英文字母与中文字母两端对齐,且英文字符需要做到均分:

可以通过两种方案实现:

方案一:

通过 flex 布局的 justify-content: space-between;属性实现两端对齐,项目之间的间隔都相等。

<div class="jd">
    <i>J</i>D<i>G</i><i>W</i>
</div>
.jd {
    display: flex;
}
.jd i {
    width: 1em;
    justify-content: space-between;
}

方案二(黑科技):

需要均分的字符间需要有一个空格进行字符之间的分离,结合 text-align: justify; 属性以及伪元素。

<div class="jd">J D G W</div>
.jd {
    display: block;
    text-align: justify;
}
.jd::after {
    content: '';
    display: inline-block;
    width: 100%;
}

前端观测:inline-block元素与line元素的垂直对齐关系

inline-block与line的垂直对齐关系很有意思,需要分成两种情况来说。

空文本 inline-block

一行文本在垂直方向上有四条准线:顶线 中线 基线 底线。相对应 vertical-align 的 top middle baseline bottom 四个值。

空文本 inline-block 节点的 baseline 和 bottom 两条线重合,如下:

<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <title>测试</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
  <meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="apple-mobile-web-app-status-bar-style" content="black" />
  <meta name="format-detection" content="telephone=no" />
  <style>
.lh_1{
  background-color: red;
  font-size: 14px;
  line-height: 1;
}
.lh_12{
  background-color: blue;
  font-size: 14px;
  line-height: 1.2;
}
.lh_15{
  background-color: green;
  font-size: 14px;
  line-height: 1.5;
}
.lh_2{
  background-color: yellow;
  font-size: 14px;
  line-height: 2;
}
.lh_3{
  background-color: yellow;
  font-size: 14px;
  /*line-height: 3;*/
  height: 42px;
  width: 14px;
  display: inline-block;
  vertical-align: baseline;
}
.gray{
  padding: 0 10px;
  background-color: #999;
}
  </style>
</head>
<body ontouchstart>
<div class="gray">
<span class="lh_1">文案</span>
<span class="lh_12">文案</span>
<span class="lh_15">文案</span>
<span class="lh_2">文案</span>
<span class="lh_3"></span>
</div>
</body>
</html>

观察结果如下:

BUG1

.lh3 修改成:

.lh_3{
  background-color: yellow;
  font-size: 14px;
  /*line-height: 3;*/
  height: 42px;
  width: 14px;
  display: inline-block;
  vertical-align: bottom;
}

观察结果如下:

BUG1

从观察很容易得出: ** 空文本inline-block 的基线和底线重合 **

文本 inline-block

当inline-block带文本时,inline-block的baseline将会继承自它内部的文本的baseline,如下:

<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <title>测试</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
  <meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="apple-mobile-web-app-status-bar-style" content="black" />
  <meta name="format-detection" content="telephone=no" />
  <style>
.lh_1{
  background-color: red;
  font-size: 14px;
  line-height: 1;
}
.lh_12{
  background-color: blue;
  font-size: 14px;
  line-height: 1.2;
}
.lh_15{
  background-color: green;
  font-size: 14px;
  line-height: 1.5;
}
.lh_2{
  background-color: yellow;
  font-size: 14px;
  line-height: 2;
}
.lh_3{
  background-color: yellow;
  font-size: 14px;
  /*line-height: 3;*/
  height: 42px;
  width: 32px;
  display: inline-block;
  vertical-align: baseline;
}
.gray{
  padding: 0 10px;
  background-color: #999;
}
  </style>
</head>
<body ontouchstart>
<div class="gray">
<span class="lh_1">文案</span>
<span class="lh_12">文案</span>
<span class="lh_15">文案</span>
<span class="lh_2">文案</span>
<span class="lh_3">文案</span>
</div>
</body>
</html>

观察如下:

BUG1

很明显,最右边的inline-block内的文本与左边三个文本是垂直对齐的,所以可以肯定:文本 inline-block 的baseline 继承自它内部的文本。

还可以进一步推论到:文本 inline-block 的baseline继承自它内部最后一行文本的baseline

更改一下 .lh3的内容

<span class="lh_3"><br />案</span>

观察结果如下:

BUG1

结论

inline-block的baseline继承自内部最后一行文本的baseline。

其它

本文没有介绍其它三条线,是因为这三条线跟我们的想象是一样的,顶线,中线和底线对应inline-block在y轴上的 0%,50%,100%,它们不受文本的影响。

png 转 webp 的正确姿势

png图片压缩

常用的压缩工具:tinypng腾讯智图 等。

图片经过多次压缩后会达到极限,并且压缩后的图片可能受损以至无法使用!如下:

png的极限压缩并不能带来高质量的图片,适应压缩才是王道。

想得到高质量低体积的图片,可以考虑使用 webp。

webp

WebP(发音 weppy,项目主页),是一种支持有损压缩和无损压缩的图片文件格式,派生自图像编码格式 VP8。根据 Google 的测试,无损压缩后的 WebP 比 PNG 文件少了 45% 的文件大小,即使这些 PNG 文件经过其他压缩工具压缩之后,WebP 还是可以减少 28% 的文件大小。

来源: ISUX -- WebP 探寻之路

webp官网给出的文档压缩率并没有这么高,如下:

WebP lossless images are 26% smaller in size compared to PNGs. WebP lossy images are 25-34% smaller than comparable JPEG images at equivalent SSIM quality index.

来源:A new image format for the Web

按官方说法,png转成webp最少也能减少 25%的文件大小。

以下是webp各浏览器的支持情况:

来源: [caniuse](http://caniuse.com/#search=webp)

另外,微信手Q(X5)的支持情况如下:

来源:[How well x5 browser support HTML5?](http://res.imtt.qq.com/qqbrowser_x5/h5/h5_support.htm)

android 4.0以上和X5内核全面支持 webp格式,IOS暂时不支持

Webp 转换工具

线上工具:腾讯智图 、ppms-photo(http://ppms1.jd.com/photo)
本地工具:WebP Converter

WebP Converter 的安装请参见:https://developers.google.com/speed/webp/

mac 可用 homebrew 安装:

sudo brew install webp

使用以下指令转换 webp

cwebp -q 80 image.png -o image.webp

具体参见:https://developers.google.com/speed/webp/docs/cwebp

ppms-photo

ppms-photo(http://ppms1.jd.com/photo)自带了图片转换webp格式的功能。只需要在上传图片后,在图片地址的后缀加上 .webp 即可以得到webp格式的图片。

以下是图片 1.png (372K) 上传到 ppms-photo 后生成的图片

http://img11.360buyimg.com/jdphoto/s750x1206_jfs/t2911/75/1468163299/372024/bdff05c6/5785a30aN0a87d9a7.png

添加 .webp,得到webp格式的图片

http://img11.360buyimg.com/jdphoto/s750x1206_jfs/t2911/75/1468163299/372024/bdff05c6/5785a30aN0a87d9a7.png.webp

ppms-photo 有一个强大的功能:按质量取webp图

http://img11.360buyimg.com/jdphoto/s750x1206_jfs/t2911/75/1468163299/372024/bdff05c6/5785a30aN0a87d9a7.png.webp 为例,

如果想获取质量为75%的图片,可以在.png.webp前加!75获得:

http://img11.360buyimg.com/jdphoto/s750x1206_jfs/t2911/75/1468163299/372024/bdff05c6/5785a30aN0a87d9a7!q75.png.webp

不在过观察过程中发现,ppms-photo转换webp时质量存在一个质量上限值:80%。可以 智图 、ppms-photo or WebP Converter 这个章节中看到数据。

WebP推荐质量75%

webp默认是有损压缩的,默认质量 75%,同时也是推荐值。

原图 与 75%WebP 对比:

图片对比
来源:智图

本人观察不出 webp 的质量损失。

质量100%WebP 与 质量75%WebP 文件大小对比:

原图: 1.png 372K
质量100%WebP: 1.png 104K
质量75%WebP: 1.png 16K

图片对比

视觉对比无差别而文件大小却相差 6~7倍,这个观察结果足使 75% 做为推荐值。

智图 、ppms-photo or WebP Converter

WebP Converter: 0.4.3
智图:2016.07.12(没有版本号,取日期)
ppms-photo:2016.07.12(没有版本号,取日期)

智图与WebP Converter的转换率如下:

质量 智图 ppms-photo WebPConverter 质量 智图 ppms-photo WebPConverter
100% - 18.9K 104K - - - -
95% 83.2K 19000B 49552B 60% 22.8K 14224B 14224B
90% 62.7K 19000B 31718B 55% 20.2K 13458B 13458B
85% 43.4K 19000B 23716B 50% 18.8K 12842B 12842B
80% 40.7K 19000B 19000B 40% 14.6K 11490B 11490B
75% 36K 16188B 16188B 30% 14.2K 10180B 10180B
70% 30.6K 15434B 15434B 20% 14K 8960B 8960B
65% 25K 14684B 14684B 10% 13.2K 7528B 7528B

原图: 1.png (372K)

ppms-photo跟WebP Converter转换率一样(ppms-photo使用的是WebP Converter),WebP Converter 全面优于智图。
建议使用 WebP Converter 或 ppms-photo 来做为 Webp的转换工具

压缩对webp的影响

按官方或是ISUX描述,可能会有这样的观点:压缩后的图片转webp可得到更小的图片。

做个简单验证:

图片: 1.png (372K)
压缩工具:tinypng
webp: WebP Converter 和 智图

压缩次数 png webp webp(智图)
0 372K 15.80K 36K
3 172K 18.55K 70.2K
6 156K 26.28K 108.3K
9 141K 33.32K 140.5K
12 134K 37.37K 152.3K
本节webp的质量为75%

通过上面的数据,得到一个相反的结论:webp文体大小与图片压缩次数成正比

这个验证同时又一次证明智图是一个很差的webp工具!转换的结果是对ISUX的打脸

结论

使用官方的 WebP Converter 做为转换工具,并且图片转换前不要做任何图片压缩!

分享一个简单好使的切图方法

不知道各位的切图方法是怎么样的,之前我的方式是把不需要的图层隐藏,然后一点点拖动裁剪工具、四周空出1px,然后保存,如果图层比较多,会先把图层拖到新文件,再裁剪保存;还有其他方法比如command+鼠标左键选中图层、command+c、新建文件然后粘贴、再保存;也有借助切图插件比如cutterman的,但是cutterman不能设置图像周围空出1px,这在rem单位的时候还挺重要的……

都挺麻烦的,其实有更简单快捷的方式切图。

ps2015之后的版本已经提供直接导出图层的功能,而且功能还挺强大,所以可以淘汰类似cutterman的切图插件。

使用方法:
右键图层(图层组)——导出为——弹出面板设置——保存

并且,可以设置图像大小,可以等比缩放icon,适配多倍屏;

还可以设置画布大小,避免单数宽高、设置每个icon四周都留出1px的空白——比如宽度是69,调整为71后ps会自动把1px分布在两边,如果调整为72,则分配左边1px,右边2px。这样通过自动化合并的icon就不会出现被“吃掉”1、2px的问题了

注意:

1、一般比较大的图层点击“导出为”后会一直loading,可以“合并图层”命令先合并再“导出为”

2、如果icon由几个图层组成而设计同学(💣)又没做好分组,那这个方式就不太好使了,要选中多个图层合并后再“导出为”

3、如果用扩展屏幕的同学“导出为”弹窗会像如下这样的话,请安装“sizeup窗口管理app”,“control + option + command + ↓”即可把窗口下移。这个app非常好用,可以快捷键使任意app窗口上下左右分屏、全屏,不买license也可以用。

【mac系统】文件名加“@2x”小工具

大家切图保存的时候相信经常会忘记添加“@2x”字符,所以用mac自带的Automator工具写了一个小工具,可以方便批量添加“@2x”.

下载地址:http://storage.360buyimg.com/mtd/home/-2x-workflow1477988002223.zip

安装:双击文件安装

卸载:打开/Users/[you name]/Library/Services/,删除文件就行

用法:选中文件(或文件夹,可多选)——右键——服务——“文件名添加@2x

适用于:Finder和PathFinder,PF在服务里找不到的话请重启系统

想用快捷键的同学可以这样设置:

设置——键盘——快捷键——服务——找到“文件名添加@2x”,添加快捷键即可

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.