cherry

cherry 查看完整档案

上海编辑河南理工大学  |  软件工程 编辑  |  填写所在公司/组织 cherryblog.site 编辑
编辑

前端工程师

个人动态

cherry 关注了用户 · 2019-12-02

mrcode @mrcode

BUG Maker & Barcelona fan & Front-end developer at Bytedance Inc & Graduated in ChongQing university of Technology.

关注 24

cherry 关注了问题 · 2017-11-05

解决关于Vue中的 render: h => h(App) 具体是什么含义?

刚开始学Vue,再看别人的例子时经常会看到

render: h => h(App)

这样一行代码,但是有的例子也没有这行代码也运行正常。去官方看文档 找到render function 没有太理解,求大神讲解这行代码的作用和为什么要这样做。谢谢!

关注 32 回答 10

cherry 关注了用户 · 2017-09-09

一只会飞的猪 @liutong

如果你想现在正在周而复始地一遍又一遍做着重复的工作,如果你想要更高的空间来施展你的才华。

不管你属于图形化前端,还是服务端前端,还是客户端(pc)前端,还是热爱技术(Java / Script or Go)的同学。

来跟我们干点儿大事!

微信或者联系邮箱: aflyingpig97,console90@163.com.

核桃编程,欢迎你的加入!Tanks for your reading.

关注 45

cherry 提出了问题 · 2017-08-27

解决为什么 zepto 源码中检测数组要同时使用 isArray 和 instanceof。

在阅读 Zepto 源码的时候,发现它使用的检测是否为数组的方法如下:

isArray = Array.isArray ||
            function(object){ return object instanceof Array };

想问一个为什么要这样设置,只使用 isArray 或者只是用 instanceof 不行么?

其次是我 google 了一下,大部分都是使用原型链的方法,所以这几种方式有什么区别?

谢谢

关注 2 回答 1

cherry 赞了文章 · 2017-08-03

Canvas getContext("3d")?

前言

不好意思,标题其实是开了个玩笑。大家都知道,Canvas 获取绘画上下文的 api 是 getContext("2d")。我第一次看到这个 api 定义的时候,就很自然的认为,既然有 2d 那一定是有 3d 的咯? 但是我接着我看到了 api 介绍的这句话

提示:在未来,如果 canvas 标签扩展到支持 3D 绘图,getContext() 方法可能允许传递一个 "3d" 字符串参数。

what? 我有一句妈卖批不知当讲不当讲... 从接触 canvas 之后我就一直等这个未来,等到后来我学习 three.js... 再等到现在,这个 getContext("3d") 还是没有出来。可能是因为越来越多浏览器都已经支持 webGL 的原因把,这个 getContext("3d") 有可能再也不会来了。

clipboard.png

webGL 就是浏览器端的 3D 绘图标准,它直接借助系统显卡来渲染 3D 场景,它能制作的 3D 应用,是普通 canvas 无法相比的。所以,你有复杂的 3D 前端项目,且不考虑 IE 的兼容性的话。不用说,直接使用 webGL 吧。

不使用 webGL 制作简单的 3D 效果

然而,有的时候我们只需要实现简单的 3D 效果。在没有学习 webGL 或这方面的框架的情况下,我们其实也可以在普通的 canvas api 基础上制作出来。而且,我们可以兼容 IE 9。先来看看,我们都能做些什么效果。

https://www.meizu.com/products/pro6/summary.html
clipboard.png

https://www.meizu.com/products/pro6/performance.html
clipboard.png

这的两个效果都是工作时简单的 3D 效果需求,没有必要使用 webGL。然而当时我并没有使用今天介绍的办法,因为没有扩展到 3D 坐标去实现所以只能很繁琐的转换成 2D 平面图形分析出来。

clipboard.png


clipboard.png

如果当时能使用今天介绍方法,将可以很简单、在很短时间就能实现。

素描知识的启发

因为平时以前在学校的时候学习过素描,现在平常也会简单画一点,所以对素描知识我有一点点了解。画画描绘真实世界的三维场景,需要用到透视。这里我当然不介绍太多,简单来说就是我们理解的近大远小,可以用简单的线条连接表示出来。两条平行的直线在无穷远的地方看起来会汇集到一起,而汇集的点,在透视里称作消失点。通过找到这个消失点,还有平行线,就可以画出简单的立体感觉的图像。

clipboard.png

观察上面这幅图,在这里所画的三维空间,所有的直线都是垂直与画面的,也就是所,如果用坐标描述每条直线上的任一点 v(x,y,z) 他们的 x,y 都是相等的。在画面上,离我们眼睛观察点越远的点,就越趋向与眼睛观察点的 x,y 。 那三维空间的坐标 v(x,y,z),对应到平面的坐标 p(x',y') 其中这个 x,y 会随着 z 的变化,是不是会呈现一定的规律对应到 x', y' 呢?

回忆中学物理课

我想起了中学学习过的一节物理课。小孔成像

clipboard.png

三维空间的火焰,透过小孔,在二维成像屏上显示了二维的画面。那时候老师教我们,这其实最简单的照相机,和我们眼睛一样,光透过瞳孔,最终到达视网膜,在转换成我们看到的影像。照相机模拟我们的眼睛,所以拍出来的照片和我们眼睛看到的感觉是一样的。

我们试着把刚才的实验转换到简单的几何坐标中看。

clipboard.png

观察 yz (x=0) 截面,假设小孔为坐标原点 (0,0,0) 成像屏到小孔的距离为 d,图中火焰上的一个点 a(0,y,z) 投射到成像屏对应点 a2,可以求的 a2 在成像屏中的平面坐标:x2 = 0, y2 = y * (d/z)。我天,这么简单就找到了这个对应关系? 先别急,为了方便开发,我们还需要做一点小转换。

像 CSS 3D 一样表示坐标

在 CSS 3D transform 中,我们需要定义 perspective 属性,用来说明观察点到屏幕的距离。如果一个点的 z 轴是 0, 那这个点是处于二维点一样的位置。z 轴越小(远离屏幕),对应到屏幕上的显示的点 xy 就越趋向于定义 perspective 属性容器的中心,也就是观察点、眼睛对应到屏幕的 xy。我们的目标就是用这种 CSS 3D 的方式表示三维的坐标(z = 0 的时候三维坐标的 xy 是和屏幕坐标的 xy 一样的),然后再套用我们找到的公式,计算出对应到屏幕中的二维坐标是多少,然后我们就可以用三维坐标描述点的位置,真正在 canvas 绘画的时候呢,通过简单的转换,用计算出来的二维坐标绘画。

clipboard.png

上一步求的 a2 对应的平面坐标是倒立的(成像屏的火焰也是倒过来的),我们可以想想在小孔与成像屏前方等距的位置放置显示屏,我们像 CSS 3D 一样,让坐标系原点就是显示屏的中点。而小孔,就成了我们的观察点,既眼睛所在的位置,眼睛离显示屏的距离就是 p(perspective)。由全等三角形的知识可以知道,上图中 a2' 刚好是 a2 正过来的坐标。咦,看来屏幕坐标完全可以简化三维坐标点和眼睛的连线与屏幕的交点。这样,一个三维空间的点坐标对应到屏幕坐标的关系就找出来了。

将这个关系用一个缩放值表示

既然已经描述出来这个关系了,我们再用把它表示成简单的公式。以便直接在代码中完成三维坐标到平面坐标的转换。

clipboard.png

已知观察者到屏幕的距离 p (perspective), 三维空间一个点的坐标 a(x,y,z),求这个点在屏幕上的坐标。 图中,三维坐标 a 在坐标 xy 平面上的向量长度 d 和该点对应到屏幕上的点 a2' 在 xy 平面上的向量长度 d',根据相似三角形,有这样的关系:

d'/d = p/(p+z)

x 和 y 的值同理:

x'/x = p/(p+z) y'/y = p/(p+z)

原来,三维空间的点坐标的 x 和 y 对应到屏幕平面上是关于 z 和 p 成比例变化的这个比例值就是

scale = p/(p+z)

这个 scale 随着物体到屏幕的距离的值的变大而变小。这也很好地解释了为什么我们看东西会近大远小的原因:

clipboard.png

缩放值的使用实例

假设我们的眼睛看的就是屏幕中央,我们现在在 y = cvs.height + 5 的 xz 平面上一个正方形区域画一系列的变长为 5 的矩形点。如果不做处理,那么可以想到我们直接使用些点的 x, y 坐标画的点,肯定在画布上是看不到的,因为范围超出了画布。而真实的世界里,我们是可以看到远处的点的,远处的点是趋向与屏幕中央的。

代码 1:

let cvs = document.querySelector('canvas');
let ctx = cvs.getContext('2d');

class Point {
    constructor(x, y, z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

// 根据 perspective 和 z 获取三维坐标对应二维坐标的xy缩放值
function getScaleByZ(z, p=600) {
    let scale; 
    if (z > p) {
        scale = Infinity;
    } else {
        scale = p / (-z + p);
    }
    return scale;
}

function draw() {
    ctx.clearRect(0,0,cvs.width,cvs.height);
    
    let rectWidth = 5;
    points.forEach((point)=>{
        let scale = getScaleByZ(point.z);
        let drawX = center.x + (point.x -  center.x) * scale;
        let drawY = center.y + (point.y -  center.y) * scale;
        let drawWidth = rectWidth * scale

        ctx.fillStyle = '#abcdef';
        ctx.fillRect(drawX, drawY, drawWidth, drawWidth);
    });
}


let center = new Point(cvs.width/2, cvs.height/2, 0);
let points = [];
let xCount = 20; // x 方向的点数
let zCount = 20; // z 方向的点数
let step = cvs.width / xCount; // x 方向点之间的间隔

for (let i = -(xCount - 1) / 2; i <= (xCount - 1) / 2; i++) {
    for (let j = -(zCount - 1) / 2; j <= (zCount - 1) / 2; j++) {

        let x = i;
        let z = j;
        let y = 0;

        console.log(x,y,z);

        points.push(
            new Point((x + xCount/2) * step, cvs.height + 1, z * step)
        );
    }
}

draw();

效果 1:

clipboard.png

在 draw 方法里,我把三维的坐标转换成了屏幕坐标。并且,边长也根据缩放值重新计算了,远处的点,边长越小。代码最终运行的结果是我们可以看到远处的点,还是有 3D 的感觉的,不过不是很明显。我们改变生成点的逻辑,这一次,我们生成一个球面上的点。

代码 2:

let center = new Point(cvs.width/2, cvs.height/2, 0);
let points = [];
let circlePointCount = 30;
let angelStep = Math.PI * 2 / circlePointCount;
let radius = 10;
let step = 40;

for (let i = -radius; i <= radius; i++) {
    let y = i;

    for (let j = 0; j < circlePointCount; j++) {

        let xzRadius = Math.sqrt(radius * radius - y * y);
        let xzAngel = j * angelStep;
        let x = xzRadius * Math.cos(xzAngel);
        let z = xzRadius * Math.sin(xzAngel);

        // console.log(x,y,z);

        points.push(
            new Point(
                x * step + cvs.width/2, 
                y * step + cvs.height/2, 
                z * step - cvs.width/2
            )
        );
    }
}
draw();

效果 2

clipboard.png

或者,再直接让它旋转起来。

代码 3

function update(angelOffset) {
    points = [];
    for (let i = -radius; i <= radius; i++) {
        let y = i;

        for (let j = 0; j < circlePointCount; j++) {

            let xzRadius = Math.sqrt(radius * radius - y * y);
            let xzAngel = j * angelStep + angelOffset;
            let x = xzRadius * Math.cos(xzAngel);
            let z = xzRadius * Math.sin(xzAngel);

            // console.log(x,y,z);
            points.push(
                new Point(
                    x * step + cvs.width/2, 
                    y * step + cvs.height/2, 
                    z * step - cvs.width/2
                )
            );
        }
    }
}

(function() {
    let angelOffset = 0;
    function tick() {
        update(angelOffset += 0.006);
        draw();
        window.requestAnimationFrame(tick);
    }
    tick();
})();

效果 3

clipboard.png

F3.js

因为学过 three.js,three.js 有丰富的三维向量计算 api。我从源码里提取了这些计算向量的 api 再结合这篇文章里总结的转换方法计算二维的坐标写了一个专门在 canvas(2d) 上绘制三维场景的组件,因为是并非真的是调用3D api,所以我取名字叫 F3.js (fake3D)

https://github.com/gnauhca/f3.js

使用 F3.js 制作的简单的 Demo:

clipboard.png

查看原文

赞 8 收藏 15 评论 3

cherry 赞了文章 · 2017-08-03

Canvas getContext("3d")?

前言

不好意思,标题其实是开了个玩笑。大家都知道,Canvas 获取绘画上下文的 api 是 getContext("2d")。我第一次看到这个 api 定义的时候,就很自然的认为,既然有 2d 那一定是有 3d 的咯? 但是我接着我看到了 api 介绍的这句话

提示:在未来,如果 canvas 标签扩展到支持 3D 绘图,getContext() 方法可能允许传递一个 "3d" 字符串参数。

what? 我有一句妈卖批不知当讲不当讲... 从接触 canvas 之后我就一直等这个未来,等到后来我学习 three.js... 再等到现在,这个 getContext("3d") 还是没有出来。可能是因为越来越多浏览器都已经支持 webGL 的原因把,这个 getContext("3d") 有可能再也不会来了。

clipboard.png

webGL 就是浏览器端的 3D 绘图标准,它直接借助系统显卡来渲染 3D 场景,它能制作的 3D 应用,是普通 canvas 无法相比的。所以,你有复杂的 3D 前端项目,且不考虑 IE 的兼容性的话。不用说,直接使用 webGL 吧。

不使用 webGL 制作简单的 3D 效果

然而,有的时候我们只需要实现简单的 3D 效果。在没有学习 webGL 或这方面的框架的情况下,我们其实也可以在普通的 canvas api 基础上制作出来。而且,我们可以兼容 IE 9。先来看看,我们都能做些什么效果。

https://www.meizu.com/products/pro6/summary.html
clipboard.png

https://www.meizu.com/products/pro6/performance.html
clipboard.png

这的两个效果都是工作时简单的 3D 效果需求,没有必要使用 webGL。然而当时我并没有使用今天介绍的办法,因为没有扩展到 3D 坐标去实现所以只能很繁琐的转换成 2D 平面图形分析出来。

clipboard.png


clipboard.png

如果当时能使用今天介绍方法,将可以很简单、在很短时间就能实现。

素描知识的启发

因为平时以前在学校的时候学习过素描,现在平常也会简单画一点,所以对素描知识我有一点点了解。画画描绘真实世界的三维场景,需要用到透视。这里我当然不介绍太多,简单来说就是我们理解的近大远小,可以用简单的线条连接表示出来。两条平行的直线在无穷远的地方看起来会汇集到一起,而汇集的点,在透视里称作消失点。通过找到这个消失点,还有平行线,就可以画出简单的立体感觉的图像。

clipboard.png

观察上面这幅图,在这里所画的三维空间,所有的直线都是垂直与画面的,也就是所,如果用坐标描述每条直线上的任一点 v(x,y,z) 他们的 x,y 都是相等的。在画面上,离我们眼睛观察点越远的点,就越趋向与眼睛观察点的 x,y 。 那三维空间的坐标 v(x,y,z),对应到平面的坐标 p(x',y') 其中这个 x,y 会随着 z 的变化,是不是会呈现一定的规律对应到 x', y' 呢?

回忆中学物理课

我想起了中学学习过的一节物理课。小孔成像

clipboard.png

三维空间的火焰,透过小孔,在二维成像屏上显示了二维的画面。那时候老师教我们,这其实最简单的照相机,和我们眼睛一样,光透过瞳孔,最终到达视网膜,在转换成我们看到的影像。照相机模拟我们的眼睛,所以拍出来的照片和我们眼睛看到的感觉是一样的。

我们试着把刚才的实验转换到简单的几何坐标中看。

clipboard.png

观察 yz (x=0) 截面,假设小孔为坐标原点 (0,0,0) 成像屏到小孔的距离为 d,图中火焰上的一个点 a(0,y,z) 投射到成像屏对应点 a2,可以求的 a2 在成像屏中的平面坐标:x2 = 0, y2 = y * (d/z)。我天,这么简单就找到了这个对应关系? 先别急,为了方便开发,我们还需要做一点小转换。

像 CSS 3D 一样表示坐标

在 CSS 3D transform 中,我们需要定义 perspective 属性,用来说明观察点到屏幕的距离。如果一个点的 z 轴是 0, 那这个点是处于二维点一样的位置。z 轴越小(远离屏幕),对应到屏幕上的显示的点 xy 就越趋向于定义 perspective 属性容器的中心,也就是观察点、眼睛对应到屏幕的 xy。我们的目标就是用这种 CSS 3D 的方式表示三维的坐标(z = 0 的时候三维坐标的 xy 是和屏幕坐标的 xy 一样的),然后再套用我们找到的公式,计算出对应到屏幕中的二维坐标是多少,然后我们就可以用三维坐标描述点的位置,真正在 canvas 绘画的时候呢,通过简单的转换,用计算出来的二维坐标绘画。

clipboard.png

上一步求的 a2 对应的平面坐标是倒立的(成像屏的火焰也是倒过来的),我们可以想想在小孔与成像屏前方等距的位置放置显示屏,我们像 CSS 3D 一样,让坐标系原点就是显示屏的中点。而小孔,就成了我们的观察点,既眼睛所在的位置,眼睛离显示屏的距离就是 p(perspective)。由全等三角形的知识可以知道,上图中 a2' 刚好是 a2 正过来的坐标。咦,看来屏幕坐标完全可以简化三维坐标点和眼睛的连线与屏幕的交点。这样,一个三维空间的点坐标对应到屏幕坐标的关系就找出来了。

将这个关系用一个缩放值表示

既然已经描述出来这个关系了,我们再用把它表示成简单的公式。以便直接在代码中完成三维坐标到平面坐标的转换。

clipboard.png

已知观察者到屏幕的距离 p (perspective), 三维空间一个点的坐标 a(x,y,z),求这个点在屏幕上的坐标。 图中,三维坐标 a 在坐标 xy 平面上的向量长度 d 和该点对应到屏幕上的点 a2' 在 xy 平面上的向量长度 d',根据相似三角形,有这样的关系:

d'/d = p/(p+z)

x 和 y 的值同理:

x'/x = p/(p+z) y'/y = p/(p+z)

原来,三维空间的点坐标的 x 和 y 对应到屏幕平面上是关于 z 和 p 成比例变化的这个比例值就是

scale = p/(p+z)

这个 scale 随着物体到屏幕的距离的值的变大而变小。这也很好地解释了为什么我们看东西会近大远小的原因:

clipboard.png

缩放值的使用实例

假设我们的眼睛看的就是屏幕中央,我们现在在 y = cvs.height + 5 的 xz 平面上一个正方形区域画一系列的变长为 5 的矩形点。如果不做处理,那么可以想到我们直接使用些点的 x, y 坐标画的点,肯定在画布上是看不到的,因为范围超出了画布。而真实的世界里,我们是可以看到远处的点的,远处的点是趋向与屏幕中央的。

代码 1:

let cvs = document.querySelector('canvas');
let ctx = cvs.getContext('2d');

class Point {
    constructor(x, y, z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

// 根据 perspective 和 z 获取三维坐标对应二维坐标的xy缩放值
function getScaleByZ(z, p=600) {
    let scale; 
    if (z > p) {
        scale = Infinity;
    } else {
        scale = p / (-z + p);
    }
    return scale;
}

function draw() {
    ctx.clearRect(0,0,cvs.width,cvs.height);
    
    let rectWidth = 5;
    points.forEach((point)=>{
        let scale = getScaleByZ(point.z);
        let drawX = center.x + (point.x -  center.x) * scale;
        let drawY = center.y + (point.y -  center.y) * scale;
        let drawWidth = rectWidth * scale

        ctx.fillStyle = '#abcdef';
        ctx.fillRect(drawX, drawY, drawWidth, drawWidth);
    });
}


let center = new Point(cvs.width/2, cvs.height/2, 0);
let points = [];
let xCount = 20; // x 方向的点数
let zCount = 20; // z 方向的点数
let step = cvs.width / xCount; // x 方向点之间的间隔

for (let i = -(xCount - 1) / 2; i <= (xCount - 1) / 2; i++) {
    for (let j = -(zCount - 1) / 2; j <= (zCount - 1) / 2; j++) {

        let x = i;
        let z = j;
        let y = 0;

        console.log(x,y,z);

        points.push(
            new Point((x + xCount/2) * step, cvs.height + 1, z * step)
        );
    }
}

draw();

效果 1:

clipboard.png

在 draw 方法里,我把三维的坐标转换成了屏幕坐标。并且,边长也根据缩放值重新计算了,远处的点,边长越小。代码最终运行的结果是我们可以看到远处的点,还是有 3D 的感觉的,不过不是很明显。我们改变生成点的逻辑,这一次,我们生成一个球面上的点。

代码 2:

let center = new Point(cvs.width/2, cvs.height/2, 0);
let points = [];
let circlePointCount = 30;
let angelStep = Math.PI * 2 / circlePointCount;
let radius = 10;
let step = 40;

for (let i = -radius; i <= radius; i++) {
    let y = i;

    for (let j = 0; j < circlePointCount; j++) {

        let xzRadius = Math.sqrt(radius * radius - y * y);
        let xzAngel = j * angelStep;
        let x = xzRadius * Math.cos(xzAngel);
        let z = xzRadius * Math.sin(xzAngel);

        // console.log(x,y,z);

        points.push(
            new Point(
                x * step + cvs.width/2, 
                y * step + cvs.height/2, 
                z * step - cvs.width/2
            )
        );
    }
}
draw();

效果 2

clipboard.png

或者,再直接让它旋转起来。

代码 3

function update(angelOffset) {
    points = [];
    for (let i = -radius; i <= radius; i++) {
        let y = i;

        for (let j = 0; j < circlePointCount; j++) {

            let xzRadius = Math.sqrt(radius * radius - y * y);
            let xzAngel = j * angelStep + angelOffset;
            let x = xzRadius * Math.cos(xzAngel);
            let z = xzRadius * Math.sin(xzAngel);

            // console.log(x,y,z);
            points.push(
                new Point(
                    x * step + cvs.width/2, 
                    y * step + cvs.height/2, 
                    z * step - cvs.width/2
                )
            );
        }
    }
}

(function() {
    let angelOffset = 0;
    function tick() {
        update(angelOffset += 0.006);
        draw();
        window.requestAnimationFrame(tick);
    }
    tick();
})();

效果 3

clipboard.png

F3.js

因为学过 three.js,three.js 有丰富的三维向量计算 api。我从源码里提取了这些计算向量的 api 再结合这篇文章里总结的转换方法计算二维的坐标写了一个专门在 canvas(2d) 上绘制三维场景的组件,因为是并非真的是调用3D api,所以我取名字叫 F3.js (fake3D)

https://github.com/gnauhca/f3.js

使用 F3.js 制作的简单的 Demo:

clipboard.png

查看原文

赞 8 收藏 15 评论 3

cherry 发布了文章 · 2017-06-02

前端开发规范:命名规范、html规范、css规范、js规范

本文首发于我的个人网站:http://cherryblog.site/ (背景更换了不知道大家有没有发现呢,嘻嘻)

一个好的程序员肯定是要能书写可维护的代码,而不是一次性的代码,怎么能让团队当中其他人甚至一段时间时候你再看你某个时候写的代码也能看懂呢,这就需要规范你的代码了。
我是有一点强迫症的人,上周我们后端给我了一个CanUsename的接口(该接口的目的是判断输入的目的地是否是4级目的地),我真的是崩溃的。
我只是觉得这个名字不够语义化,但是让我自己想一个名字我又想不出来,于是我就在想,如果有一套命名规范的话,那么以后起名字就不用发愁了,直接按照规范来就好了~
于是端午在家就百度了一下~

命名

驼峰式命名法介绍

  • Pascal Case 大驼峰式命名法:首字母大写。eg:StudentInfo、UserInfo、ProductInfo

  • Camel Case 小驼峰式命名法:首字母小写。eg:studentInfo、userInfo、productInfo

文件资源命名

  • 文件名不得含有空格

  • 文件名建议只使用小写字母,不使用大写字母。( 为了醒目,某些说明文件的文件名,可以使用大写字母,比如README、LICENSE。 )

  • 文件名包含多个单词时,单词之间建议使用半角的连词线 ( - ) 分隔。

  • 引入资源使用相对路径,不要指定资源所带的具体协议 ( http:, https: ) ,除非这两者协议都不可用。

不推荐:

<script data-original="http://cdn.com/foundation.min.js"></script>

推荐

<script data-original="//cdn.com/foundation.min.js"></script>

变量命名

命名方式 : 小驼峰式命名方法
命名规范 : 类型+对象描述的方式,如果没有明确的类型,就可以使前缀为名词

类型小写字母
arraya
booleanb
functionfn
inti
objecto
regularr
strings

推荐

var tableTitle = "LoginTable"

不推荐

var getTitle = "LoginTable"

函数

命名方式 : 小驼峰方式 ( 构造函数使用大驼峰命名法 )
命名规则 : 前缀为动词

动词含义返回值
can判断是否可执行某个动作 ( 权限 )函数返回一个布尔值。true:可执行;false:不可执行
has判断是否含有某个值函数返回一个布尔值。true:含有此值;false:不含有此值
is判断是否为某个值函数返回一个布尔值。true:为某个值;false:不为某个值
get获取某个值函数返回一个非布尔值
set设置某个值无返回值、返回是否设置成功或者返回链式对象

推荐:

//是否可阅读
function canRead(){
    return true;
}

//获取姓名
function getName{
    return this.name
}

常量

命名方法 : 全部大写
命名规范 : 使用大写字母和下划线来组合命名,下划线用以分割单词。
推荐:

 var MAX_COUNT = 10;
 var URL = 'http://www.baidu.com';

类的成员

  • 公共属性和方法 : 同变量命名方式

  • 私有属性和方法 : 前缀为下划线(_)后面跟公共属性和方法一样的命名方式

推荐(将name换成this是不是更熟悉了呢)

function Student(name) {
    var _name = name; // 私有成员
 
    // 公共方法
    this.getName = function () {
        return _name;
    }
 
    // 公共方式
    this.setName = function (value) {
        _name = value;
    }
}
var st = new Student('tom');
st.setName('jerry');
console.log(st.getName()); // => jerry:输出_name私有变量的值

注释规范

单行注释 ( // )

  • 单独一行://(双斜线)与注释文字之间保留一个空格

  • 在代码后面添加注释://(双斜线)与代码之间保留一个空格,并且//(双斜线)与注释文字之间保留一个空格。

  • 注释代码://(双斜线)与代码之间保留一个空格。
    推荐 :

// 调用了一个函数;1)单独在一行
setTitle();
 
var maxCount = 10; // 设置最大量;2)在代码后面注释
 
// setName(); // 3)注释代码

多行注释 ( / 注释说明 / )

  • 若开始(/*和结束(*/)都在一行,推荐采用单行注释

  • 若至少三行注释时,第一行为/*,最后行为*/,其他行以*开始,并且注释文字与*保留一个空格。
    推荐 :

/*
* 代码执行到这里后会调用setTitle()函数
* setTitle():设置title的值
*/
setTitle();

函数 ( 方法 ) 注释

函数(方法)注释也是多行注释的一种,但是包含了特殊的注释要求,参照 javadoc(百度百科)
语法:

/** 
* 函数说明 
* @关键字 
*/

常用注释关键字

注释名语法含义示例
@param@param 参数名 {参数类型} 描述信息描述参数的信息@param name {String} 传入名称
@return@return {返回类型} 描述信息描述返回值的信息@return {Boolean} true:可执行;false:不可执行
@author@author 作者信息 [附属信息:如邮箱、日期]描述此函数作者的信息@author 张三 2015/07/21
@version@version XX.XX.XX描述此函数的版本号@version 1.0.3
@example@example 示例代码@example setTitle('测试')

推荐 :

/**
 - 合并Grid的行
 - @param grid {Ext.Grid.Panel} 需要合并的Grid
 - @param cols {Array} 需要合并列的Index(序号)数组;从0开始计数,序号也包含。
 - @param isAllSome {Boolean} :是否2个tr的cols必须完成一样才能进行合并。true:完成一样;false(默认):不完全一样
 - @return void
 - @author polk6 2015/07/21 
 - @example
 - _________________                             _________________
 - |  年龄 |  姓名 |                             |  年龄 |  姓名 |
 - -----------------      mergeCells(grid,[0])   -----------------
 - |  18   |  张三 |              =>             |       |  张三 |
 - -----------------                             -  18   ---------
 - |  18   |  王五 |                             |       |  王五 |
 - -----------------                             -----------------
*/
function mergeCells(grid, cols, isAllSome) {
    // Do Something
}

HTML规范

文档规范

使用 HTML5 的文档声明类型 : <!DOCTYPE html>

  • DOCTYPE标签是一种标准通用标记语言的文档类型声明,它的目的是要告诉标准通用标记语言解析器,它应该使用什么样的文档类型定义(DTD)来解析文档。

  • 使用文档声明类型的作用是为了防止开启浏览器的怪异模式。

  • 没有DOCTYPE文档类型声明会开启浏览器的怪异模式,浏览器会按照自己的解析方式渲染页面,在不同的浏览器下面会有不同的样式。

  • 如果你的页面添加了<!DOCTYP>那么,那么就等同于开启了标准模式。浏览器会按照W3C标准解析渲染页面。

脚本加载

说到js和css的位置,大家应该都知道js放在下面,css放在上面。
但是,如果你的项目只需要兼容ie10+或者只是在移动端访问,那么可以使用HTML5的新属性async,将脚本文件放在<head>
兼容老旧浏览器(IE9-)时
脚本引用写在 body 结束标签之前,并带上 async 属性。这虽然在老旧浏览器中不会异步加载脚本,但它只阻塞了 body 结束标签之前的 DOM 解析,这就大大降低了其阻塞影响。
而在现代浏览器中
脚本将在 DOM 解析器发现 body 尾部的 script 标签才进行加载,此时加载属于异步加载,不会阻塞 CSSOM(但其执行仍发生在 CSSOM 之后)。
综上所述,
所有浏览器中推荐:

<html>
  <head>
    <link rel="stylesheet" href="main.css">
  </head>
  <body>
    <!-- body goes here -->
 
    <script data-original="main.js" async></script>
  </body>
</html>

只兼容现代浏览器推荐:

<html>
  <head>
    <link rel="stylesheet" href="main.css">
    <script data-original="main.js" async></script>
  </head>
  <body>
    <!-- body goes here -->
  </body>
</html>

语义化

我们一直都在说语义化编程,语义化编程,但是在代码中很少有人完全使用正确的元素。使用语义化标签也是有理由SEO的。

语义化是指:根据元素其被创造出来时的初始意义来使用它。

意思就是用正确的标签干正确的事,而不是只有divspan

不推荐:

<b>My page title</b>
<div class="top-navigation">
  <div class="nav-item"><a href="#home">Home</a></div>
  <div class="nav-item"><a href="#news">News</a></div>
  <div class="nav-item"><a href="#about">About</a></div>
</div>
 
<div class="news-page">
  <div class="page-section news">
    <div class="title">All news articles</div>
    <div class="news-article">
      <h2>Bad article</h2>
      <div class="intro">Introduction sub-title</div>
      <div class="content">This is a very bad example for HTML semantics</div>
      <div class="article-side-notes">I think I'm more on the side and should not receive the main credits</div>
      <div class="article-foot-notes">
        This article was created by David <div class="time">2014-01-01 00:00</div>
      </div>
    </div>
 
    <div class="section-footer">
      Related sections: Events, Public holidays
    </div>
  </div>
</div>
 
<div class="page-footer">
  Copyright 2014
</div>

推荐

html 代码:
<!-- The page header should go into a header element -->
<header>
  <!-- As this title belongs to the page structure it's a heading and h1 should be used -->
  <h1>My page title</h1>
</header>
 
<!-- All navigation should go into a nav element -->
<nav class="top-navigation">
  <!-- A listing of elements should always go to UL (OL for ordered listings) -->
  <ul>
    <li class="nav-item"><a href="#home">Home</a></li>
    <li class="nav-item"><a href="#news">News</a></li>
    <li class="nav-item"><a href="#about">About</a></li>
  </ul>
</nav>
 
<!-- The main part of the page should go into a main element (also use role="main" for accessibility) -->
<main class="news-page" role="main">
  <!-- A section of a page should go into a section element. Divide a page into sections with semantic elements. -->
  <section class="page-section news">
    <!-- A section header should go into a section element -->
    <header>
      <!-- As a page section belongs to the page structure heading elements should be used (in this case h2) -->
      <h2 class="title">All news articles</h2>
    </header>
 
    <!-- If a section / module can be seen as an article (news article, blog entry, products teaser, any other
     re-usable module / section that can occur multiple times on a page) a article element should be used -->
    <article class="news-article">
      <!-- An article can contain a header that contains the summary / introduction information of the article -->
      <header>
        <!-- As a article title does not belong to the overall page structure there should not be any heading tag! -->
        <div class="article-title">Good article</div>
        <!-- Small can optionally be used to reduce importance -->
        <small class="intro">Introduction sub-title</small>
      </header>
 
      <!-- For the main content in a section or article there is no semantic element -->
      <div class="content">
        <p>This is a good example for HTML semantics</p>
      </div>
      <!-- For content that is represented as side note or less important information in a given context use aside -->
      <aside class="article-side-notes">
        <p>I think I'm more on the side and should not receive the main credits</p>
      </aside>
      <!-- Articles can also contain footers. If you have footnotes for an article place them into a footer element -->
      <footer class="article-foot-notes">
        <!-- The time element can be used to annotate a timestamp. Use the datetime attribute to specify ISO time
         while the actual text in the time element can also be more human readable / relative -->
        <p>This article was created by David <time datetime="2014-01-01 00:00" class="time">1 month ago</time></p>
      </footer>
    </article>
 
    <!-- In a section, footnotes or similar information can also go into a footer element -->
    <footer class="section-footer">
      <p>Related sections: Events, Public holidays</p>
    </footer>
  </section>
</main>
 
<!-- Your page footer should go into a global footer element -->
<footer class="page-footer">
  Copyright 2014
</footer>

alt标签不为空

<img> 标签的 alt 属性指定了替代文本,用于在图像无法显示或者用户禁用图像显示时,代替图像显示在浏览器中的内容。
假设由于下列原因用户无法查看图像,alt 属性可以为图像提供替代的信息:

  • 网速太慢

  • src 属性中的错误

  • 浏览器禁用图像

  • 用户使用的是屏幕阅读器

从SEO角度考虑,浏览器的爬虫爬不到图片的内容,所以我们要有文字告诉爬虫图片的内容

结构、表现、行为三者分离

尽量在文档和模板中只包含结构性的 HTML;而将所有表现代码,移入样式表中;将所有动作行为,移入脚本之中。
在此之外,为使得它们之间的联系尽可能的小,在文档和模板中也尽量少地引入样式和脚本文件。
建议:

  • 不使用超过一到两张样式表

  • 不使用超过一到两个脚本(学会用合并脚本)

  • 不使用行内样式(<style>.no-good {}</style>

  • 不在元素上使用 style 属性(<hr style="border-top: 5px solid black">

  • 不使用行内脚本(<script>alert('no good')</script>

  • 不使用表象元素(i.e. <b>, <u>, <center>, <font>, <b>

  • 不使用表象 class 名(i.e. red, left, center

HTML只关注内容

  • HTML只显示展示内容信息

  • 不要引入一些特定的 HTML 结构来解决一些视觉设计问题

  • 不要将 img 元素当做专门用来做视觉设计的元素

  • 样式上的问题应该使用css解决

不推荐:

<!-- We should not introduce an additional element just to solve a design problem  -->
<span class="text-box">
  <span class="square"></span>
  See the square next to me?
</span>
css 代码:
.text-box > .square {
  display: inline-block;
  width: 1rem;
  height: 1rem;
  background-color: red;
}

推荐

html 代码:
<!-- That's clean markup! -->
<span class="text-box">
  See the square next to me?
</span>
css 代码:
/* We use a :before pseudo element to solve the design problem of placing a colored square in front of the text content */
.text-box:before {
  content: "";
  display: inline-block;
  width: 1rem;
  height: 1rem;
  background-color: red;
}

图片和 SVG 图形能被引入到 HTML 中的唯一理由是它们呈现出了与内容相关的一些信息。

不推荐

html 代码:
<!-- Content images should never be used for design elements!  -->
<span class="text-box">
  <img data-original="square.svg" alt="Square" />
  See the square next to me?
</span>

推荐

html 代码:
<!-- That's clean markup! -->
<span class="text-box">
  See the square next to me?
</span>
css 代码:
/* We use a :before pseudo element with a background image to solve the problem */
.text-box:before {
  content: "";
  display: inline-block;
  width: 1rem;
  height: 1rem;
  background: url(square.svg) no-repeat;
  background-size: 100%;
}

js规范

避免全局命名空间污染

防止全局命名空间被污染,我们通常的做法是将代码包裹成一个 IIFE(Immediately-Invoked Function Expression),创建独立隔绝的定义域。也使得内存在执行完后立即释放。

IIFE 还可确保你的代码不会轻易被其它全局命名空间里的代码所修改(i.e. 第三方库,window 引用,被覆盖的未定义的关键字等等)。
不推荐:

var x = 10,
    y = 100;
 
// Declaring variables in the global scope is resulting in global scope pollution. All variables declared like this
// will be stored in the window object. This is very unclean and needs to be avoided.
console.log(window.x + ' ' + window.y);

推荐

// We declare a IIFE and pass parameters into the function that we will use from the global space
(function(log, w, undefined){
  'use strict';
 
  var x = 10,
      y = 100;
 
  // Will output 'true true'
  log((w.x === undefined) + ' ' + (w.y === undefined));
 
}(window.console.log, window));

推荐的IIFE写法:

(function(){
  'use strict';
 
  // Code goes here
 
}());

如果你想引用全局变量或者是外层 IIFE 的变量,可以通过下列方式传参:

(function($, w, d){
  'use strict';
 
  $(function() {
    w.alert(d.querySelectorAll('div').length);
  });
}(jQuery, window, document));

严格模式

ECMAScript 5 严格模式可在整个脚本或独个方法内被激活。它对应不同的 javascript 语境会做更加严格的错误检查。严格模式也确保了 javascript 代码更加的健壮,运行的也更加快速。

严格模式会阻止使用在未来很可能被引入的预留关键字。

你应该在你的脚本中启用严格模式,最好是在独立的 IIFE 中应用它。避免在你的脚本第一行使用它而导致你的所有脚本都启动了严格模式,这有可能会引发一些第三方类库的问题。

变量声明

总是使用 var 来声明变量。如不指定 var,变量将被隐式地声明为全局变量,例如

var a = b = 0; //b会被隐式的创建为全局变量

所以,请总是使用 var 来声明变量,并且使用单var模式(将所有的变量在函数最前面只使用一个var定义)。例如:

(function (){
  'use strict'
  var a = 0,
      b = 0,
      c = 0,
      i,
      j,
      myObject();
}())

采用严格模式带来的好处是,当你手误输入错误的变量名时,它可以通过报错信息来帮助你定位错误出处。

js声明提前

javascript会自动将函数作用域内的变量和方法的定义提前(只是提前声明,赋值还是在原处)
例如:

(function(log){
  'use strict';
 
  var a = 10;
 
  for(var i = 0; i < a; i++) {
    var b = i * i;
    log(b);
  }
 
  if(a === 10) {
    var f = function() {
      log(a);
    };
    f();
  }
 
  function x() {
    log('Mr. X!');
  }
  x();
 
}(window.console.log));

提升后的js

(function(log){
  'use strict';
  // All variables used in the closure will be hoisted to the top of the function
  var a,
      i,
      b,
      f;
  // All functions in the closure will be hoisted to the top
  function x() {
    log('Mr. X!');
  }
 
  a = 10;
 
  for(i = 0; i < a; i++) {
    b = i * i;
    log(b);
  }
 
  if(a === 10) {
    // Function assignments will only result in hoisted variables but the function body will not be hoisted
    // Only by using a real function declaration the whole function will be hoisted with its body
    f = function() {
      log(a);
    };
    f();
  }
 
  x();
 
}(window.console.log));

使用严格等

总是使用 === 精确的比较操作符,避免在判断的过程中,由 JavaScript 的强制类型转换所造成的困扰。例如:

(function(log){
  'use strict';
 
  log('0' == 0); // true
  log('' == false); // true
  log('1' == true); // true
  log(null == undefined); // true
 
  var x = {
    valueOf: function() {
      return 'X';
    }
  };
 
  log(x == 'X');
 
}(window.console.log));

等同== 和严格等===的区别

  • ==, 两边值类型不同的时候,要先进行类型转换,再比较。

  • ===,不做类型转换,类型不同的一定不等。

==等同操作符

  • 如果两个值具有相同类型,会进行===比较,返回===的比较值

  • 如果两个值不具有相同类型,也有可能返回true

  • 如果一个值是null另一个值是undefined,返回true

  • 如果一个值是string另个是number,会把string转换成number再进行比较

  • 如果一个值是true,会把它转成1再比较,false会转成0

console.log( false == null )      // false
console.log( false == undefined ) // false
console.log( false == 0 )         // true
console.log( false == '' )        // true
console.log( false == NaN )       // false
 
console.log( null == undefined ) // true
console.log( null == 0 )         // false
console.log( null == '' )        // false
console.log( null == NaN )       // false
 
console.log( undefined == 0)   // false
console.log( undefined == '')  // false
console.log( undefined == NaN) // false
 
console.log( 0 == '' )  // true
console.log( 0 == NaN ) // false

总结一下==

  • false 除了和自身比较为 true 外,和 0,"" 比较也为 true

  • null 只和 undefined 比较时为 true, 反过来 undefined 也仅和 null 比较为 true,没有第二个

  • 0 除了和 false 比较为 true,还有空字符串 ''" 和空数组 []

  • 空字符串 '' 除了和 false 比较为 true,还有一个数字 0

==, >, <, +, -, ... 这些操作符所造成的隐式类型转换都是无副作用的,它不会改变变量本身保存的值。,但是,如果你覆写某个对象的 `valueOf/toString
`的话,==就会产生副作用.

例如:

Array.prototype.valueOf = function() {
  this[0]++;
  return this;
}
var x = [1, 2, 3];
x == 0;
console.log(x);   // [2, 2, 3]

===操作符:

  • 要是两个值类型不同,返回false

  • 要是两个值都是number类型,并且数值相同,返回true

  • 要是两个值都是stirng,并且两个值的String内容相同,返回true

  • 要是两个值都是true或者都是false,返回true

  • 要是两个值都是指向相同的Object,Arraya或者function,返回true

  • 要是两个值都是null或者都是undefined,返回true

## 真假判断

  • js中以下内容为假:

  • false

  • null

  • undefined

  • '' (空字符串)

  • NaN

设置默认参数

辑操作符 || 和 && 也可被用来返回布尔值。如果操作对象为非布尔对象,那每个表达式将会被自左向右地做真假判断。基于此操作,最终总有一个表达式被返回回来。这在变量赋值时,是可以用来简化你的代码的。例如:如果x不存在且y不存在,x=1;如果x存在y存在,x = y

if(!x) {
  if(!y) {
    x = 1;
  } else {
    x = y;
  }
}

等同于:

x = x || y || 1;

这一小技巧经常用来给方法设定默认的参数。

(function(log){
  'use strict';
 
  function multiply(a, b) {
    a = a || 1;
    b = b || 1;
 
    log('Result ' + a * b);
  }
 
  multiply(); // Result 1
  multiply(10); // Result 10
  multiply(3, NaN); // Result 3
  multiply(9, 5); // Result 45
 
}(window.console.log));

不使用eval()函数

就如eval的字面意思来说,恶魔,使用eval()函数会带来安全隐患。
eval()函数的作用是返回任意字符串,当作js代码来处理。

this关键字

只在对象构造器、方法和在设定的闭包中使用 this 关键字。this 的语义在此有些误导。它时而指向全局对象(大多数时),时而指向调用者的定义域(在 eval 中),时而指向 DOM 树中的某一节点(当用事件处理绑定到 HTML 属性上时),时而指向一个新创建的对象(在构造器中),还时而指向其它的一些对象(如果函数被 call() 和 apply() 执行和调用时)。

正因为它是如此容易地被搞错,请限制它的使用场景:

  • 在构造函数中

  • 在对象的方法中(包括由此创建出的闭包内)

首选函数式风格

函数式编程让你可以简化代码并缩减维护成本,因为它容易复用,又适当地解耦和更少的依赖。

接下来的例子中,在一组数字求和的同一问题上,比较了两种解决方案。第一个例子是经典的程序处理,而第二个例子则是采用了函数式编程和 ECMA Script 5.1 的数组方法。
不推荐

(function(log){
  'use strict';
 
  var arr = [10, 3, 7, 9, 100, 20],
      sum = 0,
      i;
 
 
  for(i = 0; i < arr.length; i++) {
    sum += arr[i];
  }
 
  log('The sum of array ' + arr + ' is: ' + sum)
 
}(window.console.log));

推荐(函数式编程):

(function(log){
  'use strict';
 
  var arr = [10, 3, 7, 9, 100, 20];
 
  var sum = arr.reduce(function(prevValue, currentValue) {
    return prevValue + currentValue;
  }, 0);
 
  log('The sum of array ' + arr + ' is: ' + sum);
 
}(window.console.log));

修改内建对象的原型链

修改内建的诸如 Object.prototypeArray.prototype 是被严厉禁止的。修改其它的内建对象比如 Function.prototype,虽危害没那么大,但始终还是会导致在开发过程中难以 debug 的问题,应当也要避免。

三元条件判断(if 的快捷方法)

用三元操作符分配或返回语句。在比较简单的情况下使用,避免在复杂的情况下使用。没人愿意用 10 行三元操作符把自己的脑子绕晕。
不推荐:

if(x === 10) {
  return 'valid';
} else {
  return 'invalid';
}

推荐:

return x === 10 ? 'valid' : 'invalid'

JSHint

在js规范中,有很多规范都是样式上的规范而不是逻辑上的规范,比如尽量使用===而不是==,我们可以使用JSHint或者JSLint,Javascript代码验证工具,这种工具可以检查你的代码并提供相关的代码改进意见。我个人使用的是JSHint,所以就以这个为例

webstorm内置JSHint

对于ws爱好者来说,我没有用过其他的编译器,ws基本上能满足你的所有需求(最新的ws集成了vue)。
在Settings => language & frameworks => JavaScript => Code Quality Tolls => JSHint
webstorm中的jshint这些规范都是什么意思呢,这里列出一些常用的,剩下的大家可以参考官方文档

名称含义
curly循环或者条件语句必须使用花括号包住
eqeqeq使用强制等===
newcap对于首字母大写的函数(声明的类),强制使用new
noarg禁用arguments.caller和arguments.callee
sub对于属性使用aaa.bbb而不是aaa['bbb']
undef查找所有未定义的变量
boss查找类似与if(a = 0)这样的代码
node指定运行环境为node
strict必须使用严格模式
asi允许省略分号
bitwise禁止使用位运算符,比如经常把&&写错& 规避此错误
jquery定义全局暴露的jQuery库
evil禁止使用eval
maxdepth嵌套的最大深度
maxparams参数的最大个数

css规范

id和class的命名

ID和class的名称总是使用可以反应元素目的和用途的名称,或其他通用的名称,代替表象和晦涩难懂的名称
不推荐 :

.fw-800 {
  font-weight: 800;
}
 
.red {
  color: red;
}

推荐 :

.heavy {
  font-weight: 800;
}
 
.important {
  color: red;
}

合理的使用ID

一般情况下ID不应该被用于样式,并且ID的权重很高,所以不使用ID解决样式的问题,而是使用class
不推荐:

#content .title {
  font-size: 2em;
}

推荐:

.content .title {
  font-size: 2em;
}

css选择器中避免使用标签名

从结构、表现、行为分离的原则来看,应该尽量避免css中出现HTML标签,并且在css选择器中出现标签名会存在潜在的问题。

使用子选择器

很多前端开发人员写选择器链的时候不使用 直接子选择器(注:直接子选择器和后代选择器的区别)。
有时,这可能会导致疼痛的设计问题并且有时候可能会很耗性能。
然而,在任何情况下,这是一个非常不好的做法。
如果你不写很通用的,需要匹配到DOM末端的选择器, 你应该总是考虑直接子选择器。
不推荐:

.content .title {
  font-size: 2rem;
}

推荐

.content > .title {
  font-size: 2rem;
}

尽量使用缩写属性

尽量使用缩写属性对于代码效率和可读性是很有用的,比如font属性。
不推荐:

border-top-style: none;
font-family: palatino, georgia, serif;
font-size: 100%;
line-height: 1.6;
padding-bottom: 2em;
padding-left: 1em;
padding-right: 1em;
padding-top: 0;

推荐:

border-top: 0;
font: 100%/1.6 palatino, georgia, serif;
padding: 0 1em 2em;

0后面不带单位

省略0后面的单位,
不推荐:

padding-bottom: 0px;
margin: 0em;

推荐:

padding-bottom: 0;
margin: 0;

属性格式

  • 为了保证一致性和可扩展性,每个声明应该用分号结束,每个声明换行。

  • 属性名的冒号后使用一个空格。出于一致性的原因,
    属性和值(但属性和冒号之间没有空格)的之间始终使用一个空格。

  • 每个选择器和属性声明总是使用新的一行。

  • 属性选择器或属性值用双引号(””),而不是单引号(”)括起来。

  • URI值(url())不要使用引号。

作为最佳实践,我们应该遵循以下顺序(应该按照下表的顺序):

结构性属性:

  1. display

  2. position, left, top, right etc.

  3. overflow, float, clear etc.

  4. margin, padding

表现性属性:

  • background, border etc.

  • font, text

不推荐:

 .box {
  font-family: 'Arial', sans-serif;
  border: 3px solid #ddd;
  left: 30%;
  position: absolute;
  text-transform: uppercase;
  background-color: #eee;
  right: 30%;
  isplay: block;
  font-size: 1.5rem;
  overflow: hidden;
  padding: 1em;
  margin: 1em;
}

推荐:

.box {
  display: block;
  position: absolute;
  left: 30%;
  right: 30%;
  overflow: hidden;
  margin: 1em;
  padding: 1em;
  background-color: #eee;
  border: 3px solid #ddd;
  font-family: 'Arial', sans-serif;
  font-size: 1.5rem;
  text-transform: uppercase;
}

相关文章:

查看原文

赞 5 收藏 8 评论 2

cherry 发布了文章 · 2017-05-06

移动端真机调试实战经验

本文首次发表于本人的个人博客:http://cherryblog.site/ ,欢迎大家到我的博客查看更多文章~

前言

在开发中前端免不了要进行移动端的开发,然而在电脑上看的样式和手机上还是有一定的差距的,因为手机上有顶部的状态栏和底部的菜单栏,特别是在qq内置浏览器中打开,差距还是蛮大的,所以在chrom中模拟手机显示的情况虽然有一定的效果,但是还是不能完全模拟,我们还需要在真机环境下测试。
本文介绍的调试方法有一下几种:

  • iphone+safari

  • android手机+pc

  • 微信开发者工具

  • weinre

  • 使用webstorm

  • 使用Fiddle抓包

这几种方法基本说涵盖了我们平时开发中所遇到的各种情况,各种主流设备都可以覆盖。其中最方便快捷的是使用webstorm自带的服务器,只需要一键就可以~,但是这样只能预览,不能调试。
我个人比较推荐的方法是iphone+safari或者安卓手机+pc的这种方式,比较简单方便快捷,然后根据具体的环境再选择更为合适的调试方法。
目前我认为使用weinre+fiddle是万能的,没有什么调试不了了~但是需要学习的成本也是最高的~
希望大家都能够写出漂亮的页面,不需要为调试发愁哈~

iphone+safari

之前使用的是mac,所以一直都是用的iphone+safari模拟真机环境,这种方法简单明了,只需要简单的设置一下以后都不要设置,插上数据线,打开mac上的safari就可以了,(๑•̀ㅂ•́)و,✧,但是对设备有要求,必须是iphone+mac的组合

iphone上设置

设置 → Safari → 高级 → Web 检查器 → 开。
这里写图片描述

pc端safari设置

Safari → 偏好设置 → 高级 → 在菜单栏中显示“开发”菜单
这里写图片描述

设置完之后用数据线连接电脑,然后在iphone上用打开safari需要调试的网址,然后在pc端打开safari,最上面的菜单栏中的“开发”然后就可以看到有iphone设备的名称显示然后就可以看见你在iphone中的safari中打开了哪些网址,之后就和调试网页版的一样了

android手机+pc

安卓手机只需要下载chrom浏览器,就可以再电脑上用chrom调试了,是不是很赞(づ ̄3 ̄)づ╭

  1. 首先需要装chrom浏览器

  2. 打开手机的开发者模式,一般是:设置->关于手机->版本号连按5次,之后设置菜单中会多出一个开发人员选项,进入将其中的“usb调试”打开

  3. 将手机与电脑通过usb连接,弹出对话框“是否允许usb调试”,选择确定

  4. 在手机chrom上打开要调试的页面

  5. 在电脑上打开chrom,新开一地址栏为chrome://inspect/的页面,然后就可以调试了

  6. 点击inspect弹出chrom调试工具

微信开发者工具

由于不可描述原因,有些页面只在微信里面出错,并且好多涉及到了微信相关的接口必须要使用微信环境的,比如自定义分享

前期准备

这个使用起来很方便。(我记得之前使用的时候(2016年),你要调试的页面必须是你是管理员的微信公众号下面的js安全域名下的地址),但是刚刚下载一个新版本的开发者工具(v0.7.0),现在的时间是2017年3月28日,发现好像没有这个限制了。所以这样开发起来就方便多了。

官方文档&下载地址

官方文档其中有下载地址

模拟微信环境调试

直接在地址栏输入地址就可以模拟微信环境调试,是不是很方便~这种方式可以满足大部分的需求

真机调试

在开发者工具中的移动调试中可以有详细的ios和安卓的调试方式,主要是使用代理,这里我没有调试成功,在手机微信中一直打不开网页,所以就不详细写了╥﹏╥...

---------------3.29更---------------------
我找到设置代理之后打不开网页的原因了,在使用fiddler抓包的时候也遇到了同样的问题,原来这里需要下载认证
在设置完当前网络的代理之后,在浏览器输入本机的ip地址和ip号,下载fiddler的证书

然后根据提示一步一步安装就可以了。
安装完证书就可以打开网页了

weinre

跟着我念三遍weinre大法好,weinre大法好,weinre大法好~
之前介绍的几种方式或多或少都有一些条件限制,但是weinre没有啊喂!就比如我是ios+windows的组合,就不能使用iphone+safari和安卓+pc的方式调试,所以使用weinre就可以!!
缺点就是配置起来有削微的麻烦~

什么是weinre

weinre是web inspector remote(远程web检查器)的缩写

安装weinre

目前安装weinre我了解到有两种方式:node和java两种方式

node方式安装weinre

首先确保你的电脑上有node环境,然后使用npm来安装
windows下
npm install weinre -g --registry=https://registry.npm.taobao.org
mac下
sudo npm install weinre -g --registry=https://registry.npm.taobao.org

java环境下安装weinre

首先确认你电脑上装好的java环境,然后下载weinre的jar包,上百度云盘的链接:链接: https://pan.baidu.com/s/1slRiOl3 密码: dsmp

运行weinre

node环境下

weinre --httpPort 8081 --boundHost -all-
8081是调试服务器运行的端口号,
boundHost是调试服务器绑定的ip地址或域名,默认是localhost,设置为-all-是为了在本地能使用localhost打开,在移动设备或本地环境用ip地址打开weinre调试工具

java环境下

在weinre所在文件夹的地址栏输入代码:java -jar weinre.jar --httpPort 8081 --boundHost -all-

开始调试

设置好端口之后我们在本地打开http://192.168.0.126:8081然后就可以看见weinre的基本信息

之后我们需要在需要调试的页面上加上一段script标签
<script data-original="http://192.168.0.126:8081/target/target-script-min.js#anonymous"></script>
需要改为你自己的ip地址

ip的查询方式

在cmd输入ipconfig,然后ipv4中后面跟的就是本机的ip地址

手机打开需要调试的链接

在staticWebDir目录下

本地的源文件貌似只能在staticWebDir目录下才可以访问到(这是因为在没有使用任何服务器的情况下,weinre自带有服务器,所以只能放在默认的根目录下),将你的源文件放在staticWebDir目录下,staticWebDir的目录是你安装weinre的根目录,我的是:C:\Users\supfn\AppData\Roaming\npm\node_modules\weinre\web,然后手机访问:http://192.168.0.126/contact_page/index.html,然后在电脑上打开刚刚的页面http://196.168.0.126:8081点击debug client user interface之后出现
,点击蓝色的链接,变为绿色的之后就说明链接成功了。在后面的elements和其他的tag就可以进行调试

在xampp下

因为公司的项目是在xampp下的,已经配置好了apache,可以直接在平时的项目前加上本地的ip,在手机上访问就好。
项目存放的地址是xampp\htdocs\app
修改配置:
C:\Windows\System32\drivers\etc\hosts文件下
最后一行
127.0.0.1 localhost ltrip.com fzc.com m.fzc.com m.ltrip.com
然后在C:\xampp\apache\conf\extra文件里面修改

<VirtualHost *:80>
    DocumentRoot "C:\xampp\htdocs\ltrip"
    ServerName ltrip.com 
    ServerAlias 
  <Directory "C:\xampp\htdocs\ltrip">
      Options FollowSymLinks ExecCGI
      AllowOverride All
      Order allow,deny
      Allow from all
      Require all granted
  </Directory>
</VirtualHost>

其中的ServerName ltrip.com 中的ltrip.com就代替了 "C:\xampp\htdocs\ltrip"这个路径,
所以就不需要放在staticWebDir目录下了,这样手机打开的地址就变成了:http://192.168.0.168/ltrip.com

使用webstorm

在最先开始使用weinre的时候,一直卡在一个地方,就是手机访问的地址问题,在看教程的时候我就卡在不知道怎么输入手机打开的网址,因为我是自己写的一个简单的html的demo,在本地打开的地址是使用本地的绝对路径比如file:///C:/Users/supfn/Desktop/contact_page/index.html这样子的,在手机肯定访问不到我的电脑上的路径。
这里是需要在本地搭建一个服务器,这样才能在手机访问到你电脑上的资源,通过服务器其他人也可以访问你电脑上的资源,常见的服务器有apache,使用Java的还可以用tomcat。这些使用起来都比较麻烦,这里推荐一个简单的方式,使用webstorm。
webstorm集成了debugger服务器,所以可以直接在你项目html页面的右上角点击浏览器的图标,在对应浏览器打开项目,然后将地址栏上的localhost改为你的ip地址,手机访问这个地址就可以了~
简直不要太方便!!所以webstorm真的是web开发利器,而不止是一个编辑器

使用Fiddle抓包

如果是要调试线上代码的话经常是无法再页面中直接加入script标签的,然后我们可以利用fiddler为页面设置断点,然后注入js代码,在run就可以了~
fiddler是用过改写http代理,让数据从它这通过,来监控截取到的数据。在打开fiddler的时候,就已经自动设置好了浏览器的代理了,关闭的时候,它又把代理还原了

下载fiddler

Fiddler 下载地址 :https://www.telerik.com/downl...
Fiddler 离线下载地址:http://pan.baidu.com/s/1i3NvE8P 密码:ozem

使用fiddler抓取数据包

在手机上设置同一个局域网上的代理,代理服务器设置为电脑的ip地址,端口为8888
在fiddler上,点击菜单栏中的 [Tools] –> [Fiddler Options]

点击 [Connections] ,设置代理端口是8888, 勾选 Allow remote computers to connect, 点击OK

使用weinre与fiddler组合

我们要实现的目标就是要调试线上的代码,使用fiddler在代码中注入weinre需要加上的script标签
在完成配置之后打开要调试的链接,然后在fiddler中设置断点
我们在fiddler中打下页面断点,bpafter + 想要打断点的网址
再次访问该网站,发现本条请求被block住了
这里写图片描述
然后在右边加上weinre需要的script标签<script data-original="http://192.168.0.126:8081/target/target-script-min.js#anonymous"></script>
然后点击右边代码上面绿色的run to completion就可以看到注入js的效果了,之后我们就可以在weinre中调试了~

参考文章

查看原文

赞 8 收藏 19 评论 2

cherry 发布了文章 · 2017-04-10

hexo高阶教程:next主题优化之加入网易云音乐、网易云跟帖、炫酷动态背景、自定义样式,打造属于你自己的定制化博客

前言

本篇文章是在已经搭建好gitpage+hexo的博客的前提下(不懂怎么搭建的可以参考我的另一篇博文:了解githubPages+hexo搭建博客的原理 或者利用Gitpage+hexo开发自己的博客,这两篇博文都比较详细的教大家最基础的怎么将博客搭建起来。本篇博文是使用next主题的进击版本,主要是有以下内容

  • 域名绑定,将github博客和你的独有域名绑定

  • 添加更多的menu内容

  • 添加头像

  • 定义网站个性logo

  • 自定义样式,重写默认样式,个性化定制你的博客

  • 炫酷动态背景制作

  • 添加网易云音乐

  • 添加网易云跟帖

  • 添加leancloud阅读次数统计功能

  • 添加wordcount页面字数统计

  • 添加fork me on github功能

要想最快的知道这些功能的效果,请移步我的个人博客:http://cherryblog.site/ ,顺便求个fork,大爷们看过可以评论一下,试一下新加上的网易云跟帖效果怎么样ヽ(●´ε`●)ノ
首先要说一下我使用的版本,这个是很重要的,我的博客最先创建于2016年的9月份,距离现在已经有大半年了,所以好多版本都已经进行了更新,特别是next主题集成了更多的插件,简直不要太爽\(@ ̄∇ ̄@)/

hexo v3.2.2
next v5.1.0
node v4.5.0

在改成自己想要的效果之后,对整体的hexo的next主题我有了一个大概的了解,其实next主题的最新版(5.1)已经集成了大部分我们需要的插件,只需要在主题配置文件中将默认的false改为true即可,但是我们也仍然需要知道都有哪些新的功能,最有效的方法是直接去查看官网的api:next官网这里写图片描述

授之于鱼不如授之于渔

希望我们都能够理解其源码,制作出属于自己专属的个性化博客(•̀ᴗ•́)

我们需要改的文件其实也就那么几个,大部分是不需要更改,next都已经帮我们配置好了~
默认目录结构:

.
├── .deploy
├── public
├── scaffolds
├── scripts
├── source
|   ├── _drafts
|   └── _posts
├── themes
├── _config.yml
└── package.json
  • deploy:执行hexo deploy命令部署到GitHub上的内容目录

  • public:执行hexo generate命令,输出的静态网页内容目录

  • scaffolds:layout模板文件目录,其中的md文件可以添加编辑

  • scripts:扩展脚本目录,这里可以自定义一些javascript脚本

  • source:文章源码目录,该目录下的markdown和html文件均会被hexo处理。该页面对应repo的根目录,404文件、favicon.ico文件,CNAME文件等都应该放这里,该目录下可新建页面目录。

    • drafts:草稿文章

    • posts:发布文章

  • themes:主题文件目录

  • _config.yml:全局配置文件,大多数的设置都在这里

  • package.json:应用程序数据,指明hexo的版本等信息,类似于一般软件中的关于按钮

我们最先修改的应该是在hexo根目录下的配置文件_config.yml文件,这里是配置整个站点的配置信息,在文章的最后贴出我的配置文件,有兴趣的朋友可以参考一下~
其次就是我们的主题配置文件
在对应的主题下的_config.yml 因为我使用的是next主题,所以目录的路径为C:\Hexo\themes\next\_config.yml 这里配置的是使用主题的配置文件,这个配置文件的东西就有点多了,我们大部分的修改也是在这个文件下完成的。比如说使用集成的第三方插件,默认为false,我们需要将其改为true并且配置相应的app_key就可以使用该插件了~有木有很方便(^ ◕ᴥ◕ ^)
然后我们需要修改样式的话是需要设置css和甚至是修改模板,
页面展现的全部逻辑都在每个主题中控制,源代码在hexothemes你使用的主题中,以next主题为例:

├── .github            #git信息
├── languages          #多语言
|   ├── default.yml    #默认语言
|   └── zh-Hans.yml      #简体中文
|   └── zh-tw.yml      #繁体中文
├── layout             #布局,根目录下的*.ejs文件是对主页,分页,存档等的控制
|   ├── _custom        #可以自己修改的模板,覆盖原有模板
|   |   ├── _header.swig    #头部样式
|   |   ├── _sidebar.swig   #侧边栏样式
|   ├── _macro        #可以自己修改的模板,覆盖原有模板
|   |   ├── post.swig    #文章模板
|   |   ├── reward.swig    #打赏模板
|   |   ├── sidebar.swig   #侧边栏模板
|   ├── _partial       #局部的布局
|   |   ├── head       #头部模板
|   |   ├── search     #搜索模板
|   |   ├── share      #分享模板
|   ├── _script        #局部的布局
|   ├── _third-party   #第三方模板
|   ├── _layout.swig   #主页面模板
|   ├── index.swig     #主页面模板
|   ├── page           #页面模板
|   └── tag.swig       #tag模板
├── scripts            #script源码
|   ├── tags           #tags的script源码
|   ├── marge.js       #页面模板
├── source             #源码
|   ├── css            #css源码
|   |   ├── _common    #*.styl基础css
|   |   ├── _custom    #*.styl局部css
|   |   └── _mixins    #mixins的css
|   ├── fonts          #字体
|   ├── images         #图片
|   ├── uploads        #添加的文件
|   └── js             #javascript源代码
├── _config.yml        #主题配置文件
└── README.md          #用GitHub的都知道

绑定域名

绑定域名的思路如下:

  • 在万网购买自己喜欢的域名(.com的会贵一点,.site和.xyz的相对便宜一些,有的只需要几块钱一年就可以)

  • 解析DNS

  • 在hexo中添加CNAME文件

购买域名

之前没有买域名的时候我想使用网易云跟帖,发现在注册网易云跟帖的时候使用原来的域名提示“url已被使用”,这是因为网易云跟帖不认可二级域名,所以要自己买域名。
我选择的是万网,阿里下面的。我选择了一个.site的域名,原价8元,使用阿里云app支付还优惠5元,等于3元到手一个域名(一年)~
按照官网的步骤一步一来就可以了~

解析DNS

购买完域名之后我们需要解析DNS地址,在管理控制台中的左侧有域名选项,然后找到你的域名,点击后面的“解析”
这里写图片描述

点击添加解析,记录类型选A或CNAME,

A记录的记录值就是ip地址,github(官方文档)提供了两个IP地址,192.30.252.153和192.30.252.154,这两个IP地址为github的服务器地址,两个都要填上,
解析记录设置两个www和@,线路就默认就行了,CNAME记录值填你的github博客网址。如我的是sunshine940326.github.io。

在hexo中添加CNAME文件

接下来在你的hexo文件夹下source文件夹下新建一个CANME文件,里面加上你刚刚购买的域名比如我的cherryblog.site

如果直接填写cherryblog.site的话直接访问www.cherryblog.site 和 cherryblog.site 都可以访问到我们的网站,但是如果填写www.cherryblog.site 的话只能通过www.cherryblog.site 访问,不能通过cherryblog.site 访问

这里写图片描述
然后你就可以hexo clean,hexo ghexo d 发布你的博客看看效果啦~
这里写图片描述

添加菜单页

添加菜单页的思路(添加菜单页就是添加一个页面,有两种方式):第一种是使用git命令hexo new page "photo" 就直接创建了 C:\Hexo\source\photo\index.md文件,然后编辑index.md 文件就可以了~
这里写图片描述
第二种:手动创建上面的文件= =

  • 在主题的配置文件添加menu索引路径(根路径是hexosource,所以你如果想要更改页面的内容就去hexosource下找到对应的文件夹,默认内容是在其index.md文件下)

  • 在hexo的source文件下添加对应的文件夹

  • 在主题的配置文件添加menu_icon字段设置对应的icon

  • 修改language文件下zh-hans语言包

  • 在发表文章的时候添加对应的menu字段就可以看到

刚开始的时候不理解怎么添加分类页和添加文章的区别,公司有一个项目用到了wordpress,然后发现两者有相似的地方,不同的就是wordpress是有可视化的操作后台,而hexo是需要git bash自己创建
首先我们要分清什么是页面,什么是文章,
在hexo中menu下的内容都是新的页面我们可以通过hexo new page "pagename" 创建,hexo默认的页面只有home,archives,tags 三个,之后我们写的博文就是文章,通过hexo new "name" 创建的name.md 文件在根目录的source\_posts 下,在每一个文章的头部,我们可以配置其tags或者categories内容,相当于文章是页面的下一级

在配置文件中添加menu索引路径

我们可以在主题配置的_config文件下找到相应的字段,字段前加# 表示被注释掉,我们也可以自己添加menu的内容,比如我又新增了两个menulifephoto
这里写图片描述

这里添加的字段其实是加上文件索引的路径,这里hexo设置的根路径是hexo/source 接下来我们在这个根路径下建立相应的文件夹就可以实现点击mune跳转到相应的页面上了

这里写图片描述,
没有明白什么意思的同学看下图
图片描述

在source文件添加menu文件夹

我们需要在这个路径下自己建立对应的页面,比如说我新建了menulifephotos,然后再source文件夹下面新建两个名字为lifephoto 的文件夹,里面添加一个index.md markdown文件,内容是类似这样的

---
title: photo
date: 2017-04-04 22:14:07
type: "photo"
comments: false
---
啦啦啦~

这里写图片描述
这是一个markdown文件,你可以自己编写,但是我还不知道怎么把添加html文件= =,回来研究一下

给menu添加icon

如果只是上面的步骤,那么你可能会创建出一个新的页面,但是显示的效果会是这样:这里写图片描述

怎么icon没有换???其实hexo中换icon是一个很简单的事情,因为hexo集成了FontAwsome 所以我们只需要在主题的配置文件中加入相应的icon名字即可
这里写图片描述

查找FontAwsome icon

这时候你想要换一个自己喜欢的icon怎么办,这就需要自己动手,丰衣足食了,你需要自己到FontAwsome官网,然后鼠标往下拉,在图标集中选择自己喜欢的icon,然后记住名字,保存在上面的menu_icon字段中就可以啦~
这里写图片描述tips :在字段中只需要填写icon-name后面跟的name即可,不需要加上前面的"icon-"

在language添加zh-hans翻译字段

上面的步骤完成之后你会发现,在你的博客首页显示的仍然是英文名,而我们想要有一个中文的名字,并且想要个性化定制我们的页面,我们可以在主题的language文件下的zh-hans(中文)语言包下增加相应的字段(做过翻译的童鞋应该都知道什么意思~)还可以修改其他的字段,这样就可以定制我们的博客了呢~
这里写图片描述

在发表文章的时候添加对应的menu字段

在我们写文章的时候只要在头部信息添加相应的字段就在tags页面和categories中显示相应的分类,例如:

---
title: Git使用中的报错情况
date: 2017-03-11 23:54:11
tags: [git,实战经验] 
categories: git
---

tags、categories都是支持数组的形式的,可以添加多个tags、categories。这样我们在tags、categories页面就可以看见相应的分类了
这里写图片描述

添加头像

我使用的主题头像是位于侧边栏,显示的效果如下,
这里写图片描述要添加一个这个的头像要怎么操作呢,其实思路就是将你要上传的头像放在你的文件夹中,然后再配置文件中引用正确的路径即可,当然也可以上传绝对路径。在你的主题配置文件找到avatar字段,然后将你得图片路径写在后面,我是新建了一个uploads文件夹,将图片放在下面

# Sidebar Avatar
# in theme directory(source/images): /images/avatar.jpg
# in site  directory(source/uploads): /uploads/avatar.jpg
avatar: /uploads/avatar.png

这里写图片描述

设置网站logo

跟设置头像其实是一个思路,都是在配置文件中引入正确的地址就可以了,不过网站的logo是对图片有要求的,我们需要在Favicon在线制作工具中制作32*32的.ico图片,然后放在source/images下面。然后在主题配置文件下添加主题配置文件中添加:favicon: images/favicon.ico

自定义样式

不得不说next还是很人性化的,你可以个性化定制你的网站,你所有的改动(css)需要放在主题文件的source/css/_costum/costum.styl文件中,会覆盖原来的css,所以只要你不想要你修改的样式,只需要删除这个文件夹就可以了,再也不用担心还原不回去了~
这里写图片描述

炫酷动态背景

之前做过一个类似的canvas-nest的效果。新版本的next已经支持canvas-nest了,但是效果不怎么样,就不用了,但是也介绍一下,毕竟简单,只有两步就可以了。
添加修改代码next\layout\_layout.swig</body>之前加上

{% if theme.canvas_nest %}
<script type="text/javascript" data-original="//cdn.bootcss.com/canvas-nest.js/1.0.0/canvas-nest.min.js"></script>
{% endif %}

打开next\_config.yml,添加以下代码就可以了:

 # Canvas-nest
canvas_nest: true

这种虽然简单,但是我认为效果不够好,于是我决定添加原生的js来仿知乎的登录界面做背景,这就需要修改模板来实现了。只要我们知道了next文件的结构,我们想改什么只需要找到对应的位置就好(在前言中有next的目录结构)~
这里写图片描述
所以我们需要在layout下面的_layout.swig 添加一个canvas

<div class="bg_content">
    <canvas id="canvas"></canvas>
</div>

然后使用原生js写一个仿知乎页面

  <script>
class Circle {
    //创建对象
    //以一个圆为对象
    //设置随机的 x,y坐标,r半径,_mx,_my移动的距离
    //this.r是创建圆的半径,参数越大半径越大
    //this._mx,this._my是移动的距离,参数越大移动
    constructor(x, y) {
        this.x = x;
        this.y = y;
        this.r = Math.random() * 10 ;
        this._mx = Math.random() ;
        this._my = Math.random() ;

    }

    //canvas 画圆和画直线
    //画圆就是正常的用canvas画一个圆
    //画直线是两个圆连线,为了避免直线过多,给圆圈距离设置了一个值,距离很远的圆圈,就不做连线处理
    drawCircle(ctx) {
        ctx.beginPath();
        //arc() 方法使用一个中心点和半径,为一个画布的当前子路径添加一条弧。
        ctx.arc(this.x, this.y, this.r, 0, 360)
        ctx.closePath();
        ctx.fillStyle = 'rgba(204, 204, 204, 0.3)';
        ctx.fill();
    }

    drawLine(ctx, _circle) {
        let dx = this.x - _circle.x;
        let dy = this.y - _circle.y;
        let d = Math.sqrt(dx * dx + dy * dy)
        if (d < 150) {
            ctx.beginPath();
            //开始一条路径,移动到位置 this.x,this.y。创建到达位置 _circle.x,_circle.y 的一条线:
            ctx.moveTo(this.x, this.y);   //起始点
            ctx.lineTo(_circle.x, _circle.y);   //终点
            ctx.closePath();
            ctx.strokeStyle = 'rgba(204, 204, 204, 0.3)';
            ctx.stroke();
        }
    }

    // 圆圈移动
    // 圆圈移动的距离必须在屏幕范围内
    move(w, h) {
        this._mx = (this.x < w && this.x > 0) ? this._mx : (-this._mx);
        this._my = (this.y < h && this.y > 0) ? this._my : (-this._my);
        this.x += this._mx / 2;
        this.y += this._my / 2;
    }
}
//鼠标点画圆闪烁变动
class currentCirle extends Circle {
    constructor(x, y) {
        super(x, y)
    }

    drawCircle(ctx) {
        ctx.beginPath();
        //注释内容为鼠标焦点的地方圆圈半径变化
        //this.r = (this.r < 14 && this.r > 1) ? this.r + (Math.random() * 2 - 1) : 2;
        this.r = 8;
        ctx.arc(this.x, this.y, this.r, 0, 360);
        ctx.closePath();
        //ctx.fillStyle = 'rgba(0,0,0,' + (parseInt(Math.random() * 100) / 100) + ')'
        ctx.fillStyle = 'rgba(255, 77, 54, 0.3)'
        ctx.fill();

    }
}
//更新页面用requestAnimationFrame替代setTimeout
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;

let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let w = canvas.width = canvas.offsetWidth;
let h = canvas.height = canvas.offsetHeight;
let circles = [];
let current_circle = new currentCirle(0, 0)

let draw = function () {
    ctx.clearRect(0, 0, w, h);
    for (let i = 0; i < circles.length; i++) {
        circles[i].move(w, h);
        circles[i].drawCircle(ctx);
        for (j = i + 1; j < circles.length; j++) {
            circles[i].drawLine(ctx, circles[j])
        }
    }
    if (current_circle.x) {
        current_circle.drawCircle(ctx);
        for (var k = 1; k < circles.length; k++) {
            current_circle.drawLine(ctx, circles[k])
        }
    }
    requestAnimationFrame(draw)
}

let init = function (num) {
    for (var i = 0; i < num; i++) {
        circles.push(new Circle(Math.random() * w, Math.random() * h));
    }
    draw();
}
window.addEventListener('load', init(60));
window.onmousemove = function (e) {
    e = e || window.event;
    current_circle.x = e.clientX;
    current_circle.y = e.clientY;
}
window.onmouseout = function () {
    current_circle.x = null;
    current_circle.y = null;

};
</script>

添加网易云音乐

在知道了页面的结构之后,你就可以将你的播放器添加在页面的任意位置,开始我是放在了首页,然后发现一上来就自动播放太吵了,于是就放在了侧边栏,想要听得朋友可以手动点击播放,
我们可以直接在网易云音乐中搜索我们想要插入的音乐,然后点击生成外链播放器
这里写图片描述
然后可以根据你得设置生成相应的html代码,将获得的html代码插入到你想要插入的位置即可
这里写图片描述
我放在了侧边栏,所以对应的修改layout\_macro\sidebar.swig 文件

<div id="music163player">
    <iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=280 height=86 data-original="//music.163.com/outchain/player?type=2&id=38358214&auto=0&height=66">
    </iframe>
</div>

然后就可以在侧边栏看见我的播放器了~
这里写图片描述

网易云跟帖

之前用的是多说,但是多说在2017年6月1日就关闭评论服务了= =,很忧伤,于是转到了网易云跟帖。由于最新版(5.1)版本的next已经集成了网易云跟帖,所以只需要在主题的设置文件中配置你的productKey就可以了。获取productKey也很简单,在官网网易云跟帖中注册,然后在获取代码>通用代码中拿到productKey,之后在你的主题配置文件中的gentie_productKey字段后添加即可~

添加Fork me on GitHub

去网址https://github.com/blog/273-g... 挑选自己喜欢的样式,并复制代码,添加到themesnextlayout_layout.swig的body标签之内即可
记得把里面的url换成自己的!

hexo-wordcount实现统计功能

这里写图片描述
wordcount可以实现字数统计,阅读时常还有总字数的统计功能
只需要npm install hexo-wordcount --save 就可以安装wordcount插件,
主要功能
字数统计:WordCount
阅读时长预计:Min2Read
总字数统计: TotalCount
安装完插件之后在主题的配置文件中开启该功能就可以~

# Post wordcount display settings
# Dependencies: https://github.com/willin/hexo-wordcount
post_wordcount:
  item_text: true
  wordcount: true
  min2read: true

leancloud阅读次数统计

next也集成了leancloud,在leancloud官网
中注册账号等一步一步的操作就不说了哈~,我们主要是为了拿到app_key和app_id,然后在主题配置文件做一下配置

# Show number of visitors to each article.
# You can visit https://leancloud.cn get AppID and AppKey.
leancloud_visitors:
  enable: true
  app_id: yourapp_id
  app_key: yourapp_key

然后再leancloud的控制台中的存储添加一个counter的class就可以检测到我们的浏览量了,同时在你文章的副标题也可以看到有阅读次数的显示
这里写图片描述

个人网站地址:http://cherryblog.site/,顺便求个fork

附录1:站点配置文件

 # Hexo Configuration
## Docs: https://hexo.io/docs/configuration.html
## Source: https://github.com/hexojs/hexo/

# Site   站点信息
title: Cherry's Blog    #站点名字
subtitle: To Be a Batter Me  #副标题
description: 做更好的自己   #站点描述,在侧边栏显示
author: Cherry   #博主名字
email: 991939332@qq.com #联系邮箱
language: zh-Hans   #使用的语言包,语言包在主题文件的language文件夹下,可以更改网站显示出的文案
timezone:

# URL
## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'
url: http://yoursite.com
root: /
permalink: :year/:month/:day/:title/
permalink_defaults:

# Directory
source_dir: source
public_dir: public
tag_dir: tags
archive_dir: archives
category_dir: categories
code_dir: downloads/code
i18n_dir: :lang
skip_render:

# Writing
new_post_name: :title.md # File name of new posts
default_layout: post
titlecase: false # Transform title into titlecase
external_link: true # Open external links in new tab
filename_case: 0
render_drafts: false
post_asset_folder: true  #新建一个页面后自动生成一个同名文件夹(默认为false)
relative_link: false
future: true
highlight:
  enable: true
  line_number: true
  auto_detect: false
  tab_replace:

# Category & Tag
default_category: uncategorized
category_map:
tag_map:

# Date / Time format
## Hexo uses Moment.js to parse and display date
## You can customize the date format as defined in
## http://momentjs.com/docs/#/displaying/format/
date_format: YYYY-MM-DD
time_format: HH:mm:ss

# Pagination
## Set per_page to 0 to disable pagination
per_page: 10
pagination_dir: page

# Extensions
## Plugins: https://hexo.io/plugins/
## Themes: https://hexo.io/themes/
theme: next

# Deployment
## Docs: https://hexo.io/docs/deployment.html
deploy:
  type: git
  repository: https://github.com/sunshine940326/sunshine940326.github.io.git
  #repository: ssh://git@github.com/sunshine940326/sunshine940326.github.io
查看原文

赞 11 收藏 22 评论 6

认证与成就

  • 获得 24 次点赞
  • 获得 5 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 5 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-02-15
个人主页被 509 人浏览