shinn_lancelot

shinn_lancelot 查看完整档案

嘉兴编辑浙江水利水电学院  |  软件 编辑XX科技  |  码农 编辑 hillpy.com 编辑
编辑

沈宝宝家的奶爸,喜欢ACGM的码农~

个人动态

shinn_lancelot 收藏了文章 · 6月24日

工作中99%能用到的git命令

【Git】工作中99%能用到的git命令

分支操作

  1. git branch 创建分支
  2. git checkout -b 创建并切换到新建的分支上
  3. git checkout 切换分支
  4. git branch 查看分支列表
  5. git branch -v 查看所有分支的最后一次操作
  6. git branch -vv 查看当前分支
  7. git brabch -b 分支名 origin/分支名 创建远程分支到本地
  8. git branch --merged 查看别的分支和当前分支合并过的分支
  9. git branch --no-merged 查看未与当前分支合并的分支
  10. git branch -d 分支名 删除本地分支
  11. git branch -D 分支名 强行删除分支
  12. git branch origin :分支名 删除远处仓库分支
  13. git merge 分支名 合并分支到当前分支上

暂存操作

  1. git stash 暂存当前修改
  2. git stash apply 恢复最近的一次暂存
  3. git stash pop 恢复暂存并删除暂存记录
  4. git stash list 查看暂存列表
  5. git stash drop 暂存名(例:stash@{0}) 移除某次暂存
  6. git stash clear 清除暂存

回退操作

  1. git reset --hard HEAD^ 回退到上一个版本
  2. git reset --hard ahdhs1(commit_id) 回退到某个版本
  3. git checkout -- file撤销修改的文件(如果文件加入到了暂存区,则回退到暂存区的,如果文件加入到了版本库,则还原至加入版本库之后的状态)
  4. git reset HEAD file 撤回暂存区的文件修改到工作区

标签操作

  1. git tag 标签名 添加标签(默认对当前版本)
  2. git tag 标签名 commit_id 对某一提交记录打标签
  3. git tag -a 标签名 -m '描述' 创建新标签并增加备注
  4. git tag 列出所有标签列表
  5. git show 标签名 查看标签信息
  6. git tag -d 标签名 删除本地标签
  7. git push origin 标签名 推送标签到远程仓库
  8. git push origin --tags 推送所有标签到远程仓库
  9. git push origin :refs/tags/标签名 从远程仓库中删除标签

其它操作

常规操作

  1. git push origin test 推送本地分支到远程仓库
  2. git rm -r --cached 文件/文件夹名字 取消文件被版本控制
  3. git reflog 获取执行过的命令
  4. git log --graph 查看分支合并图
  5. git merge --no-ff -m '合并描述' 分支名 不使用Fast forward方式合并,采用这种方式合并可以看到合并记录
  6. git check-ignore -v 文件名 查看忽略规则
  7. git add -f 文件名 强制将文件提交

git创建项目仓库

1、git init 初始化 2、git remote add origin url 关联远程仓库 3、git pull 4、git fetch 获取远程仓库中所有的分支到本地

忽略已加入到版本库中的文件

1、git update-index --assume-unchanged file 忽略单个文件 2、git rm -r --cached 文件/文件夹名字 (. 忽略全部文件)

取消忽略文件

git update-index --no-assume-unchanged file

拉取、上传免密码

git config --global credential.helper store

查看原文

shinn_lancelot 收藏了文章 · 1月16日

WebGL入门demo

WebGL入门demo

three.js入门

开场白

哇哦,绘制气球耶,在网页上?对啊!厉害了!3D效果图也能在网页上绘制出来啊,这么好玩的事情,赶紧来看看!

这里是属于WebGL的应用,webGL可以让我们在canvas上实现3D效果。而three.js是一款webGL框架,由于其易用性被广泛应用。如果要学习webGL,抛弃那些复杂的原生接口从这款框架入手是一个不错的选择。跟着我一起走!

?:three.js参考文档 英文

?:three.js参考文档 中文

看地球咯!

哈哈,别说了。先看地球:
地球

怎么画?

首先要理清逻辑。three.js框架是个法宝,要画东西的工具,模块,材料等等里面都有,找到API去用。所以,我们只需要:

  • 一张图片,也就是包裹地球身体的那张图片,
  • 一个球模型,
  • 把图片贴到球模型上去,地球就出来了,
  • 再给球加上一些动画效果,完工!

开始画!

上面讲完了画的大致流程,现在要开始画了。但是你还需要知道,不止这么简单!远不止这么简单!你需要:

1.设置three.js渲染器-renderer

2.设置摄像机camera

3.设置场景scene br>

4.设置物体object-地球

5.设置组织者

是不是一脸懵逼?别怕,来讲个故事?

其实,就是拍电影啦。需要相机,演员(这里是地球),场景(scene),导演(group)。导演组织这些活动,导演也要看场景的,他受场景的约束,演员也要听导演的。最后拍好了戏交给渲染器(renderer)来制片,发布。

好吧,这么形象估计懂了,来,我们具体来讲讲。

一步步画:

每个元素都是再定义了之后,在初始化函数内部执行。

做准备:

用到three.js框架,要先引入以下:

<script data-original="https://threejs.org/build/three.js"></script>
<script data-original="https://threejs.org/examples/js/renderers/Projector.js"></script>
<script data-original="https://threejs.org/examples/js/renderers/CanvasRenderer.js"></script>
<script data-original="https://threejs.org/examples/js/libs/stats.min.js"></script>

画地球:

看代码:

// 加载材质
var loader = new THREE.TextureLoader();
    loader.load('https://threejs.org/examples/textures/land_ocean_ice_cloud_2048.jpg',
     function(texture) {
        //画球体 形状
        var geometry = new THREE.SphereGeometry(200, 20, 20);
        // 贴图 材质纹理
        var material = new THREE.MeshBasicMaterial({
            map: texture,
            overdraw: 0.5
        })
        // 地球
        var mesh = new THREE.Mesh(geometry, material);
        group.add(mesh);
        }

画地球需要地球外面那张图片,还需要球模型geometry。图片需要裁剪之后变成material。再用这两个元素来new地球mesh,把地球交给group.

设置场景:

var scene;
scene = new THREE.Scene();
scene.add(group);

设置分组(导演):

var group;
group = new THREE.Group();

设置相机:

var camera;
// 准备好镜头
    camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight,1,2000);//相机摆上 设置相机摆放位置 产生镜头
    camera.position.z = 500;//拍的景物离我500px

先设置一下相机,把他放到里面去。

设置渲染器:

var renderer;
renderer = new THREE.CanvasRenderer();
        renderer.setClearColor(0xffffff);//设置canvas背景颜色
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);//container展示的大小
        container.appendChild(renderer.domElement)//追加 【canvas】 元素到 【container】 元素中
        stats = new Stats();
        container.appendChild(stats.dom);

先设置一下渲染器,设置在画布上面显示的属性,再把画布添加到浏览器页面上面去。还有在动画过程中的循环渲染在下面讲解。

加动画啦!

var container,stas;
var mouseX=0,mouseY=0;
var windowHalfX=window.innerWidth/2;
var windowHalfY=window.innerHeight/2;
animate();
document.addEventListener('mousemove', onDocumentMouseMove, false);//用鼠标拖
window.addEventListener('resize',onWindowResize,false);

function onDocumentMouseMove (event) {
    mouseX = event.clientX - windowHalfX;//鼠标基于中心点的偏移量;
    mouseY = event.clientY - windowHalfY;//鼠标基于中心点的偏移量;
}

function onWindowResize (event) {
    windowHalfX = window.innerWidth / 2;
    windowHalfY = window.innerHeight / 2;
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth,window.innerHeight);
}

function animate () {
// 每秒60针递归调用 使地球旋转
    requestAnimationFrame(animate);
    render();
    stats.update();
}
function render () {
    camera.position.x
     += (mouseX-camera.position.x)*0.05;//在x轴上,相机根据鼠标的位置移动来移动的距离
    camera.position.y 
    += (-mouseY - camera.position.y)*0.05;//在y轴上,相机根据鼠标的位置移动来移动的距离
    camera.lookAt(scene.position);//设置视野的中心坐标
    group.rotation.y -= 0.005;//让它饶着y轴旋转 (间接的得到旋转的速度)
    renderer.render(scene, camera);//将webgl视图往外输出
}

设置在鼠标动的时候监听到,而且此时camera随即改变而改变。camera要改变根据鼠标的移动来移动它的距离在函数onDocumentMouseMove中得到,而且地球要有一种远小近大的感觉。随着鼠标移动,camera变化,地球的大小也在改变,也就是前面说的远小近大的感觉。在函数onWindowResize中实现。然后地球要动画起来要调用animate函数,每秒60针递归调用 使地球旋转,然后render函数就一直在不停的循环。状态也在不停的更新。

小结:

WebGL是是一种3D绘图标准,这种绘图技术里面用了JavaScript,所以会JavaScript,走遍天下都不怕啊?

?:源码

思考好逻辑,就可以动手的!好玩就要去尝试,就在慢慢成长。大家共同进步吧!

查看原文

shinn_lancelot 收藏了文章 · 2019-08-20

瀑布流插件Masonry中文文档【翻译】

本位为Masonry官方文档翻译,原始链接

安装Install

下载

下载压缩或未压缩的masonry

  • masonry.pkgd.min.js (压缩)
  • masonry.pkgd.js (未压缩)

CDN

在unpkg直接饮用Masonry文件。

<script data-original="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
<!-- or -->
<script data-original="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.js"></script>

包管理

使用Bower安装

bower install masonry --save

使用npm安装

npm install masonry-layout

入门Getting started

HTML

在你的项目中引入Masonry.js

<script data-original="/path/to/masonry.pkgd.min.js"></script>

Masonry的运行需要一个包含一些列子组件的grid容器标签

<div class="grid">
  <div class="grid-item">...</div>
  <div class="grid-item grid-item--width2">...</div>
  <div class="grid-item">...</div>
  ...
</div>

CSS

你可以通过CSS控制所有组件的尺寸

.grid-item { width: 200px; }
.grid-item--width2 { width: 400px; }

通过jQuery初始化

你可以将Masonry作为一个jQuery插件来使用$('selector').masonry()

$('.grid').masonry({
  // options
  itemSelector: '.grid-item',
  columnWidth: 200
});

通过原生JavaScript初始化

你可以通过原生JS使用Masonry:new Masonry( elem, options )。构造函数Masonry()接收两个参数:容器标签和配置对象。

var elem = document.querySelector('.grid');
var msnry = new Masonry( elem, {
  // options
  itemSelector: '.grid-item',
  columnWidth: 200
});
// element argument can be a selector string
//   for an individual element
var msnry = new Masonry( '.grid', {
  // options
});

在HTML中初始化

你也可以在HTML中初始化Masonry,这样不需要书写任何JavaScript。在容器标签中增加data-masonry属性,其值则是Masonry组件的配置

<div class="grid" data-masonry='{ "itemSelector": ".grid-item", "columnWidth": 200 }'>

注意:在HTML中必须以JSON格式配置,key必须带引号,例如:"itemSelector":data-masonry的值使用单引号,JSON使用双引号。
在Masonry v3版本,HTML初始化需要使用特定的class: js-masonry ,设置配置需要属性data-masonry-options,在Masonry v4之后版本中,这种写法也是兼容的。

<div class="grid js-masonry"
  data-masonry-options='{ "itemSelector": ".grid-item", "columnWidth": 200 }'>

布局Layout

组件尺寸

你可以通过CSS控制组件的尺寸

div class="grid">
  <div class="grid-item"></div>
  <div class="grid-item grid-item--width2"></div>
  <div class="grid-item grid-item--height2"></div>
  ...
</div>
.grid-item {
  float: left;
  width: 80px;
  height: 60px;
  border: 2px solid hsla(0, 0%, 0%, 0.5);
}

.grid-item--width2 { width: 160px; }
.grid-item--height2 { height: 140px; }

响应式布局

组件的尺寸可设置成百分比从而适应响应式的布局,在设置masonry布局模式时,将columnWidth设置为指定组件的尺寸,设置percentPosition: true 。组件的位置将不再改变,window改变大小事,组件将以百分比的形式响应式布局。

<div class="grid">
  <!-- width of .grid-sizer used for columnWidth -->
  <div class="grid-sizer"></div>
  <div class="grid-item"></div>
  <div class="grid-item grid-item--width2"></div>
  ...
</div>
/* fluid 5 columns */
.grid-sizer,
.grid-item { width: 20%; }
/* 2 columns */
.grid-item--width2 { width: 40%; }
$('.grid').masonry({
  // set itemSelector so .grid-sizer is not used in layout
  itemSelector: '.grid-item',
  // use element for option
  columnWidth: '.grid-sizer',
  percentPosition: true
})

imagesLoaded

Masonry排列未加载完成的图片时会导致元素的重叠,imagesLoaded可以解决这个问题。imagesLoaded是一个独立的脚本插件,你可以在imagesloaded.desandro.com下载。
初始化Masonry,在每一张图片加载完成后触发布局。

// init Masonry
var $grid = $('.grid').masonry({
  // options...
});
// layout Masonry after each image loads
$grid.imagesLoaded().progress( function() {
  $grid.masonry('layout');
});

或者在所有图片都加载完成后初始化Masonry

var $grid = $('.grid').imagesLoaded( function() {
  // init Masonry after all images have loaded
  $grid.masonry({
    // options...
  });
});

配置项Options

除了columnWidthitemSelector以外,所有的配置项都是可以选择的。

// jQuery
$('.grid').masonry({
  columnWidth: 200,
  itemSelector: '.grid-item'
});
// vanilla JS (原生JS)
var msnry = new Masonry( '.grid', {
  columnWidth: 200,
  itemSelector: '.grid-item'
});
<!-- in HTML -->
<div class="grid" data-masonry='{ "columnWidth": 200, "itemSelector": ".grid-item" }'>

必选配置项Recommended

itemSelector
用于指定参与布局的子组件。
我们建议始终设置项,用于区分参组件元素是否参与布局。

itemSelector: '.grid-item'
<div class="grid">
  <!-- do not use banner in Masonry layout -->
  <!—在Masonry布局时忽略 banner
  <div class="static-banner">Static banner</div>
  <div class="grid-item"></div>
  <div class="grid-item"></div>
  ...
</div>

columnWidth
用于在水平网格上对齐组件

我们建议设置columnWidth,如果没有设置columnWidth,Masonry将使用第一个组件的外宽作为默认值。

columnWidth: 80

使用组件尺寸,在响应式布局中奖组件的宽度设置成百分比,设置columnWidth时,将值设置为指定组件选择器的字符串,即使用该组件的外宽。

div class="grid">
  <!-- .grid-sizer empty element, only used for element sizing -->
  <div class="grid-sizer"></div>
  <div class="grid-item"></div>
  <div class="grid-item grid-item--width2"></div>
  ...
</div>
/* fluid 5 columns */
.grid-sizer,
.grid-item { width: 20%; }
/* 2 columns wide */
.grid-item--width2 { width: 40%; }
// use outer width of grid-sizer for columnWidth
columnWidth: '.grid-sizer',
itemSelector: '.grid-item',
percentPosition: true

布局Layout

组件尺寸Element sizing
尺寸配置项columnWidthgutter可以可以设置组件的列宽和间距。

<div class="grid">
  <!-- .grid-sizer empty element, only used for element sizing -->
  <div class="grid-sizer"></div>
  <div class="grid-item"></div>
  <div class="grid-item grid-item--width2"></div>
  ...
</div>
/* fluid 5 columns */
.grid-sizer,
.grid-item { width: 20%; }
/* 2 columns wide */
.grid-item--width2 { width: 40%; }
// use outer width of grid-sizer for columnWidth
columnWidth: '.grid-sizer',
// do not use .grid-sizer in layout
itemSelector: '.grid-item',
percentPosition: true

该配置项可以设置为一个选择器的字符串或一个元素

// set to a selector string
// first matching element within container element will be used
columnWidth: '.grid-sizer'

// set to an element
columnWidth: $grid.find('.grid-sizer')[0]

组件尺寸允许你使用CSS控制Masonry的布局。这在响应式布局和媒体查询中非常有用。

/* 3 columns by default */
.grid-sizer { width: 33.333%; }

@media screen and (min-width: 768px) {
  /* 5 columns for larger screens */
  .grid-sizer { width: 20%; }
}

Gutter(间距)

    gutter: 10

在js配置项gutter可以设置组件的横向间距,使用CSS margin可设置组件的纵向间距。

gutter: 10

css:

.grid-item {
  margin-bottom: 10px;
}

在响应式布局中使用组件尺寸将宽度设置为百分比时,可以将gutter的值设置为选择器字符串或者一个元素。

<div class="grid">
  <div class="grid-sizer"></div>
  <div class="gutter-sizer"></div>
  <div class="grid-item"></div>
  <div class="grid-item grid-item--width2"></div>
  ...
</div>
.grid-sizer,
.grid-item { width: 22%; }
.gutter-sizer { width: 4%; }
.grid-item--width2 { width: 48%; }
columnWidth: '.grid-sizer',
gutter: '.gutter-sizer',
itemSelector: '.grid-item',
percentPosition: true

horizontalOrder
使组件按照从左到右排列。(对比组件们在第二排的排列)

horizontalOrder: true

clipboard.png
clipboard.png

percentPosition
设置组件的位置(尺寸)为百分比而非像素数。percentPosition: true可以使宽度为百分比的组件不改变他们原本的位置。

// set positions with percent values
percentPosition: true,
columnWidth: '.grid-sizer',
itemSelector: '.grid-item'
/* fluid 5 columns */
.grid-sizer,
.grid-item { width: 20%; }

Stamp
指定组件为stamp。Masonry在布局时会避开这些组件。
配置项stamp只在Masonry实例第一次初始化完成后黏贴指定组件,但你可以通过stamp method更改后续的stamp布局。

<div class="grid">
  <div class="stamp stamp1"></div>
  <div class="stamp stamp2"></div>
  <div class="grid-item"></div>
  <div class="grid-item"></div>
  ....
</div>
// specify itemSelector so stamps do get laid out
itemSelector: '.grid-item',
// stamp elements
stamp: '.stamp'
/* position stamp elements with CSS */
.stamp {
  position: absolute;
  background: orange;
  border: 4px dotted black;
}
.stamp1 {
  left: 30%;
  top: 10px;
  width: 20%;
  height: 100px;
}
.stamp2 {
  right: 10%;
  top: 20px;
  width: 70%;
  height: 30px;
}

fitWidth

根据父级容器的尺寸,设置容器的宽,以适应可用的列数。启用之后将容器grid居中

fitWidth: true
/* center container with CSS */
.grid {
  margin: 0 auto;
}

originLeft
控制水平布局,默认状态下originLeft: true控件从左到右布局,设置originLeft: false后,控件从右向左布局。
originLeftMasonry v3使用isOriginLeft,在Masonry v4之后isOriginLeft也是被兼容的。

originLeft: false

originTop
类似originLeft,开启originTop: false后,自下而上布局

设置(Setup)

containerStyle
设置父级容器grid的css样式。默认状态下为position:’relative’,禁用grid容器的所有样式:containerStyle:null

// default
// containerStyle: { position: 'relative' }

// disable any styles being set on container
// useful if using absolute position on container
containerStyle: null

transitionDuration
控件改变位置或重排的缓动时间。默认为0.4s

// fast transitions
transitionDuration: '0.2s'

// slow transitions
transitionDuration: '0.8s'

// no transitions
transitionDuration: 0

stagger
控件重排的时间。当一个控件改变了位置,其他控件逐次的改变位置进行重排,stagger属性即为每个控件发生重排的缓动时间。,默认为值30(毫秒)

stagger: 30

resize
当窗口大小改变时控件重排以适应父级容器大小。默认为允许重排resize: true,在v3版本中使用isResizeBound,并在v4版本下兼容。

// disable window resize behavior
resize: false

/* grid has fixed width */
.grid {
  width: 320px;
}

initLayout
允许初始化布局,默认开启。
设置为initLayout: false,可以禁止初始化布局,你可以通过methods或者增加event事件的方法开启布局。V3版本使用isInitLayout

var $grid = $('.grid').masonry({
  // disable initial layout
  initLayout: false,
  //...
});
// bind event
$grid.masonry( 'on', 'layoutComplete', function() {
  console.log('layout is complete');
});
// trigger initial layout
$grid.masonry();

方法(Methods)

Methods是Masonry实例的行为
使用jQuery时,methods遵从jQuery格式.masonry( 'methodName' /* arguments */ )

$grid.masonry()
  .append( elem )
  .masonry( 'appended', elem )
  // layout
  .masonry();

原生JS的method使用类似:masonry.methodName( /* arguments */ ),与jQuery不同,原生JS不能使用链(chaining).

// vanilla JS
var msnry = new Masonry( '.grid', {...});
gridElement.appendChild( elem );
msnry.appended( elem );
msnry.layout();

布局(Layout)

layout / .masonry()
将所有组件布局。layout用于当一个组件改变了尺寸后所有的控件需要重新布局。

// jQuery
$grid.masonry()
// vanilla JS
msnry.layout()
var $grid = $('.grid').masonry({
  columnWidth: 80
});
// change size of item by toggling gigante class
$grid.on( 'click', '.grid-item', function() {
  $(this).toggleClass('gigante');
  // trigger layout after item size changes
  $grid.masonry('layout');
});

layoutItems
布局指定控件

// jQuery
$grid.masonry( 'layoutItems', items, isStill )
// vanilla JS
msnry.layoutItems( items, isStill )

items Masonry控件的数组
isStill布尔型,禁止变换
stamp
在排列中黏贴指定控件,Masonry会围绕被黏贴的元素进行排列

// jQuery
$grid.masonry( 'stamp', elements )
// vanilla JS
msnry.stamp( elements )

elements element,jQuery对象,节点,或数组
设置不参与瀑布流布局的对象,以选择器形式给出。

var $grid = $('.grid').masonry({
  // specify itemSelector so stamps do get laid out
  itemSelector: '.grid-item',
  columnWidth: 80
});
var $stamp = $grid.find('.stamp');
var isStamped = false;

$('.stamp-button').on( 'click', function() {
  // stamp or unstamp element
  if ( isStamped ) {
    $grid.masonry( 'unstamp', $stamp );
  } else {
    $grid.masonry( 'stamp', $stamp );
  }
  // trigger layout
  $grid.masonry('layout');
  // set flag
  isStamped = !isStamped;
});

unstamp
解除指定元素的stamp 状态。

增加&移除控件

Appended
在瀑布流末尾增加新控件并重排。

// jQuery
$grid.masonry( 'appended', elements )
// vanilla JS
msnry.appended( elements )

elements element,jQuery对象,节点,或数组

$('.append-button').on( 'click', function() {
  // create new item elements
  var $items = $('<div class="grid-item">...</div>');
  // append items to grid
  $grid.append( $items )
    // add and lay out newly appended items
    .masonry( 'appended', $items );
});

*(注意链chaining的使用,此处为先增加节点,再讲节点重排)
jQuery可以使用,增加字符串结构的HTML节点,但是masonry不行,所以当时用jQuery ajax动态加载节点时要将HTML节点转化成jQuery对象。

// does not work
$.get( 'page2', function( content ) {
  // HTML string added, but items not added to Masonry
  $grid.append( content ).masonry( 'appended', content );
});

// does work
$.get( 'page2', function( content ) {
  // wrap content in jQuery object
  var $content = $( content );
  // add jQuery object
  $grid.append( $content ).masonry( 'appended', $content );
});

prepended
类似append,在顶部增加新节点并重排。
addItems
项Masonry实例中增加控件元素,addItems不能像append和prepended重排新增加的元素

// jQuery
$grid.masonry( 'addItems', elements )
// vanilla JS
msnry.addItems( elements )

remove
从Masonry实例和DOM中移除元素

// jQuery
$grid.masonry( 'remove', elements )
// vanilla JS
msnry.remove( elements )
$grid.on( 'click', '.grid-item', function() {
  // remove clicked element
  $grid.masonry( 'remove', this )
    // layout remaining item elements
    .masonry('layout');
});

事件(Events)

on
增加一个Masonry事件监听。

// jQuery
var msnry = $grid.masonry( 'on', eventName, listener )
// vanilla JS
msnry.on( eventName, listener )

eventName 字符串,Masonry事件名称
listener 方法
off
移除Masonry事件

// jQuery
var msnry = $grid.masonry( 'off', eventName, listener )
// vanilla JS
msnry.off( eventName, listener )

eventName 字符串,Masonry事件名称
listener 方法
once
增加一个Masonry事件,只触发一次。

// jQuery
var msnry = $grid.masonry( 'once', eventName, listener )
// vanilla JS
msnry.once( eventName, listener )

eventName 字符串,Masonry事件名称
listener 方法

$grid.masonry( 'once', 'layoutComplete', function() {
  console.log('layout is complete, just once');
})

Utilities

reloadItems
Recollects all item elements.
For frameworks like Angular and React, reloadItems may be useful to apply changes to the DOM to Masonry.

// jQuery
$grid.masonry('reloadItems')
// vanilla JS
msnry.reloadItems()

destroy
移除所有的Masonry功能,destroy将恢复元素预加载之前的状态。

// jQuery
$grid.masonry('destroy')
// vanilla JS
msnry.destroy()
var masonryOptions = {
  itemSelector: '.grid-item',
  columnWidth: 80
};
// initialize Masonry
var $grid = $('.grid').masonry( masonryOptions );
var isActive = true;

$('.toggle-button').on( 'click', function() {
  if ( isActive ) {
    $grid.masonry('destroy'); // destroy
  } else {
    $grid.masonry( masonryOptions ); // re-initialize
  }
  // set flag
  isActive = !isActive;
});

getItemElements
返回一个组件元素的数组

// jQuery
var elems = $grid.masonry('getItemElements')
// vanilla JS
var elems = msnry.getItemElements()

elems 数组
jQuery.fn.data('masonry')
从jQuery对象中取出Masonry实例,以便读取Masonry的属性。

var msnry = $('.grid').data('masonry')
// access Masonry properties
console.log( msnry.items.length + ' filtered items'  )

Masonry.data
通过元素取出Masonry实例,Masonry.data()用于从HTML初始化的Masonry实例中取出Masonry属性。

var msnry = Masonry.data( element )

element 控件或选择器的字符串
msnry Masonry

<!-- init Masonry in HTML -->
<div class="grid" data-masonry='{...}'>...</div>
// jQuery
// pass in the element, $element[0], not the jQuery object
var msnry = Masonry.data( $('.grid')[0] )

// vanilla JS
// using an element
var grid = document.querySelector('.grid')
var msnry = Masonry.data( grid )
// using a selector string
var msnry = Masonry.data('.grid')

事件

事件绑定(event binding)

jQuery事件绑定
使用jQuery标准的事件方法绑定,如.on().off().one()

// jQuery
var $grid = $('.grid').masonry({...});

function onLayout() {
  console.log('layout done');
}
// bind event listener
$grid.on( 'layoutComplete', onLayout );
// un-bind event listener
$grid.off( 'layoutComplete', onLayout );
// bind event listener to be triggered just once. note ONE not ON
$grid.one( 'layoutComplete', function() {
  console.log('layout done, just this one time');
});

jQuery事件监听器需要一个eventargument参数,原生的JS不需要。

// jQuery, has event argument
$grid.on( 'layoutComplete', function( event, items ) {
  console.log( items.length );
});

// vanilla JS, no event argument
msnry.on( 'layoutComplete', function( items ) {
  console.log( items.length );
});

原生JS事件绑定
使用原生JS方法绑定。on(),.off(),.once()。

// vanilla JS
var msnry = new Masonry( '.grid', {...});

function onLayout() {
  console.log('layout done');
}
// bind event listener
msnry.on( 'layoutComplete', onLayout );
// un-bind event listener
msnry.off( 'layoutComplete', onLayout );
// bind event listener to be triggered just once
msnry.once( 'layoutComplete', function() {
  console.log('layout done, just this one time');
});

Masonry 事件

layoutComplete
在布局和所有位置变化完成后触发。

// jQuery
$grid.on( 'layoutComplete', function( event, laidOutItems ) {...} )
// vanilla JS
msnry.on( 'layoutComplete', function( laidOutItems ) {...} )

laidOutItems Masonry控件数组,已完成排列的控件

$grid.on( 'layoutComplete',
  function( event, laidOutItems ) {
    console.log( 'Masonry layout completed on ' +
      laidOutItems.length + ' items' );
  }
);

removeComplete
元素移除后触发

// jQuery
$grid.on( 'removeComplete', function( event, removedItems ) {...} )
// vanilla JS
msnry.on( 'removeComplete', function( removedItems ) {...} )

removedItemsMasonry控件数组,被移除的控件

$grid.on( 'removeComplete',
  function( event, removedItems ) {
    notify( 'Removed ' + removedItems.length + ' items' );
  }
);
查看原文

shinn_lancelot 收藏了文章 · 2019-08-13

阻止微信浏览器下拉滑动效果(ios11.3 橡皮筋效果)

在升级到 ios11.3 系统后,发现之前阻止页面滚动的代码e.preventDefault代码失效了。于是自己折腾了一番,找到了解决办法,分享给大家。

一、前言

浏览器在移动端有一个默认触摸滚动的效果,让我们感触最深的莫过于微信浏览器里面,下拉时自带橡皮筋的效果。

然而在开发的时候我们经常需要阻止此效果。

在此先直接给一个方案,直接加上下面的代码即可:

document.body.addEventListener('touchmove', function (e) {
  e.preventDefault(); //阻止默认的处理方式(阻止下拉滑动的效果)
}, {passive: false}); //passive 参数不能省略,用来兼容ios和android

如果不加passive:false,在 ios 上是不能起作用的。

二、解释

微信在 Android 端和 IOS 端使用的不是同样的浏览器内核:

  • Android 版 微信浏览器 :QQ浏览器 X5内核(相当于使用的 Chrome)
  • IOS 版 微信浏览器 :WKWebView(相当于使用的Safari)

所以下面分别使用 Chrome 和 Safari 来分析。

关于浏览器内核问题,有兴趣的可以看看这个:浏览器内核总结

1. Chrome 默认的事件监听参数:

clipboard.png

useCapture:false 表示事件采用冒泡机制(capture 译为 捕获),浏览器默认就是false;
passive:false 表示我现在主动告诉浏览器该监听器将使用e.preventDefault()来阻止浏览器默认的滚动行为(可以提高运行速度)。

2. Safari 默认的事件监听参数:

在 Safari 中,有一个更新

Updated root document touch event listeners to use passive mode improving scrolling performance and reducing crashes
更新了根文档触摸事件侦听器,默认使用passive:true提高滚动性能并减少崩溃

所以Safari 中默认使用了passive:true,告诉浏览器,此监听事件中,不会阻止默认的页面滚动。这将导致设置的e.preventDefault()代码失效。

所以 Safari 默认是不会阻止滚动的。

3. 结论

我们通过 e.preventDefault(); 阻止默认的下拉滑动的效果,通过添加 passive:false 参数来兼容各个浏览器。即可实现阻止移动页面滚动的功能。

三、关于 passive 参数

关于 passive 在事件监听中的作用,推荐大家看这篇文章:passive 的事件监听器

查看原文

shinn_lancelot 收藏了文章 · 2019-08-02

教你用webpack搭一个vue脚手架[超详细讲解和注释!]

1.适用人群

    1.对webpack知识有一定了解但不熟悉的同学.
    
    2.女同学!!!(233333....)

2.目的

 在自己对webpack有进一步了解的同时,也希望能帮到一些刚接触webpack的同学.
脚手架已放上github,不想听我啰嗦的同学可以直接去download或clone下来看哦.

脚手架里都有详细注释!

https://github.com/webfansplz...

觉得有帮助到你的同学给个star哈,也算是对我的一种支持!

3.脚手架结构

├── build                       构建服务和webpack配置
    |—— build.js                webpack打包服务
    |—— webpack.base.conf.js    webpack基本通用配置
    |—— webpack.dev.conf.js     webpack开发环境配置
    |—— webpack.prod.conf.js    webpack生产环境配置
├── config                      构建项目不同环境的配置
├── public                      项目打包文件存放目录
├── index.html                  项目入口文件
├── package.json                项目配置文件
├── static                       静态资源
├── .babelrc                    babel配置文件
├── .gitignore                  git忽略文件
├── postcss.config.js           postcss配置文件
├── src                         项目目录
    |—— page                    页面组件目录
    |—— router                  vue路由配置
    |—— store                   vuex配置
    |—— App.vue                 vue实例入口
    |—— main.js                 项目构建入口

4.配置npm scripts

4.1 生成package.json文件,配置npm scripts.

4.1.1 使用 npm init 命令,生成一个package.json文件!

    npm init

4.1.2 全局安装webpack和webpack-dev-server

   npm install webpack webpack-dev-server -g

4.1.3 在项目目录下安装webpack和webpack-dev-server

  npm install webpack webpack-dev-server -D

4.1.4 进入package.json配置npm scripts命令

  "scripts": {
    "dev": "webpack-dev-server  --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "build": "node build/build.js"
  }

  通过配置以上命令:
  我们可以通过npm start/npm run dev在本地进行开发,
  scripts.dev命令解读:
  通过webpack-dev-server命令 启动build文件夹下webpack.dev.conf.js。
  也可以通过npm run build 打包项目文件进行线上部署.
  scripts.build命令解读:
  通过node命令构建build文件夹下的build.js。
  命令的配置可以根据自己脚手架的配置文件位置和名称不同修改哦!

5.构建脚手架目录

同学们可以通过自己的习惯和喜爱搭建自己的脚手架目录,下面讲解以上面脚手架结构为准!

6.构建config/config.js

6.1 该文件主要用来配置构建开发环境和生产环境差异化的参数.
6.2

const _path = require("path");
const ExtractTextPlugin = require("extract-text-webpack-plugin");

//vue-loader基本配置
const baseVueLoaderConf = {
  //引入postcss插件
  postcss: {
    config: {
      path: _path.resolve("../")
    }
  },
  //转为require调用,让webpack处理目标资源!
  transformToRequire: {
    video: "src",
    source: "src",
    img: "src",
    image: "xlink:href"
  }
};

//vue-loader 开发环境配置
const devVueLoaderConf = Object.assign({}, baseVueLoaderConf, {
  //loaders
  loaders: {
    css: ["vue-style-loader", "css-loader"],
    less: ["vue-style-loader", "css-loader", "postcss-loader", "less-loader"]
  },
  cssSourceMap: true
});

//vue-loader 生产环境配置
const buildVueLoaderConf = Object.assign({}, baseVueLoaderConf, {
  //loaders
  loaders: ExtractTextPlugin.extract({
    use: ["css-loader", "postcss-loader", "less-loader"],
    fallback: "vue-style-loader"
  }),
  cssSourceMap: false
});

//开发/生产环境 配置参数!
module.exports = {
  dev: {
    publicPath: "/",
    devtoolType: "cheap-module-eval-source-map",
    vueloaderConf: devVueLoaderConf,
    host: "localhost",
    port: "1234",
    proxyTable: {}
  },
  build: {
    publicPath: "/",
    devtoolType: "source-map",
    vueloaderConf: buildVueLoaderConf,
    staticPath: "static"
  }
};

7.构建build/webpack.base.conf.js

7.1 此文件主要是webpack开发环境和生成环境的通用配置.

7.2

"use strict";

//引入node path路径模块
const path = require("path");
//引入webpack生产环境配置参数
const prodConfig = require("../config").build;

//拼接路径
function resolve(track) {
  return path.join(__dirname, "..", track);
}
//资源路径
function assetsPath(_path) {
  return path.join(prodConfig.staticPath, _path);
}

//webpack 基本设置

module.exports = {
  //项目入口文件->webpack从此处开始构建!
  entry: path.resolve(__dirname, "../src/main.js"),
  //配置模块如何被解析
  resolve: {
    //自动解析文件扩展名(补全文件后缀)(从左->右)
    // import hello from './hello'  (!hello.js? -> !hello.vue? -> !hello.json)
    extensions: [".js", ".vue", ".json"],
    //配置别名映射
    alias: {
      // import Vue from 'vue/dist/vue.esm.js'可以写成 import Vue from 'vue'
      // 键后加上$,表示精准匹配!
      vue$: "vue/dist/vue.esm.js",
      "@": resolve("src"),
      utils: resolve("src/utils"),
      components: resolve("src/components"),
      public: resolve("public")
    }
  },
  module: {
    //处理模块的规则(可在此处使用不同的loader来处理模块!)
    rules: [
      //使用babel-loader来处理src下面的所有js文件,具体babel配置在.babelrc,主要是用来转义es6
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader"
        },
        include: resolve("src")
      },
      //使用url-loader(file-loader的一个再封装)对引入的图片进行编码,此处可将小于8192字节(8kb)的图片转为DataURL(base64),
      //大于limit字节的会调用file-loader进行处理!
      //图片一般发布后都是长缓存,故此处文件名加入hash做版本区分!
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: "url-loader",
        options: {
          limit: 8192,
          name: assetsPath("img/[name].[hash:8].[ext]")
        }
      }
    ]
  }
};

8.构建 build/webpack.dev.conf.js

8.1 该文件主要用于构建开发环境

8.2

"use strict";
//引入node path路径模块
const path = require("path");
//引入webpack
const webpack = require("webpack");
//引入webpack开发环境配置参数
const devConfig = require("../config").dev;
//引入webpack基本配置
const baseConf = require("./webpack.base.conf");
//一个webpack配置合并模块,可简单的理解为与Object.assign()功能类似!
const merge = require("webpack-merge");
//一个创建html入口文件的webpack插件!
const HtmlWebpackPlugin = require("html-webpack-plugin");
//一个编译提示的webpack插件!
const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");
//发送系统通知的一个node模块!
const notifier = require("node-notifier");
//将webpack基本配置与开发环境配置合并!
const devConf = merge(baseConf, {
  //项目出口,webpack-dev-server 生成的包并没有写入硬盘,而是放在内存中!
  output: {
    //文件名
    filename: "[name].js",
    //html引用资源路径,在dev-server中,引用的是内存中文件!
    publicPath: devConfig.publicPath
  },
  //生成sourceMaps(方便调试)
  devtool: devConfig.devtoolType,
  //
  //启动一个express服务器,使我们可以在本地进行开发!!!
  devServer: {
    //HMR控制台log等级
    clientLogLevel: "warning",
    // 热加载
    hot: true,
    //自动刷新
    inline: true,
    //自动打开浏览器
    open: true,
    //在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html
    historyApiFallback: true,
    //主机名
    host: devConfig.host,
    //端口号
    port: devConfig.port,
    //配置反向代理解决跨域
    proxy: devConfig.proxyTable,
    //为你的代码进行压缩。加快开发流程和优化的作用
    compress: true,
    // 在浏览器上全屏显示编译的errors或warnings。
    overlay: {
      errors: true,
      warnings: false
    },
    // 终端输出的只有初始启动信息。 webpack 的警告和错误是不输出到终端的
    quiet: true
  },
  module: {
    //处理模块的规则(可在此处使用不同的loader来处理模块!)
    rules: [
      //使用vue-loader处理以vue结尾的文件!
      {
        test: /\.vue$/,
        loader: "vue-loader",
        options: devConfig.vueloaderConf
      },
      //使用vue-style-loader!css-loader!postcss-loader处理以css结尾的文件!
      {
        test: /\.css$/,
        use: [
          "vue-style-loader",
          {
            loader: "css-loader",
            options: {
              sourceMap: true
            }
          },
          {
            loader: "postcss-loader",
            options: {
              sourceMap: true
            }
          }
        ]
      },
      //使用vue-style-loader!css-loader!postcss-loader处理以less结尾的文件!
      {
        test: /\.less$/,
        use: [
          "vue-style-loader",
          {
            loader: "css-loader",
            options: {
              sourceMap: true
            }
          },
          {
            loader: "less-loader",
            options: {
              sourceMap: true
            }
          },
          {
            loader: "postcss-loader",
            options: {
              sourceMap: true
            }
          }
        ]
      }
    ]
  },
  plugins: [
    //开启HMR(热替换功能,替换更新部分,不重载页面!)
    new webpack.HotModuleReplacementPlugin(),

    //显示模块相对路径
    new webpack.NamedModulesPlugin(),

    //编译出错时,该插件可跳过输出,确保输出资源不会包含错误!
    // new webpack.NoEmitOnErrorsPlugin(),

    //配置html入口信息
    new HtmlWebpackPlugin({
      title: "hello,xc-cli!",
      filename: "index.html",
      template: "index.html",
      //js资源插入位置,true表示插入到body元素底部
      inject: true
    }),

    //编译提示插件
    new FriendlyErrorsPlugin({
      //编译成功提示!
      compilationSuccessInfo: {
        messages: [
          `Your application is running here: http://${devConfig.host}:${devConfig.port}`
        ]
      },
      //编译出错!
      onErrors: function(severity, errors) {
        if (severity !== "error") {
          return;
        }
        const error = errors[0];
        const filename = error.file.split("!").pop();
        //编译出错时,右下角弹出错误提示!
        notifier.notify({
          title: "xc-cli",
          message: severity + ": " + error.name,
          subtitle: filename || "",
          icon: path.join(__dirname, "xc-cli.png")
        });
      }
    })
  ]
});
module.exports = devConf;

8.3 通过创建以上文件,并下载相应的依赖和创建项目入口,我们就可以通过npm run dev在本地开发vue项目啦!!!

9.创建 build/webpack.prod.conf.js

9.1 此文件主要用于构建生产环境的配置.
9.2

"use strict";
//引入node path路径模块
const path = require("path");
//引入webpack
const webpack = require("webpack");
//一个webpack配置合并模块,可简单的理解为与Object.assign()功能类似!
const merge = require("webpack-merge");
//引入webpack生产环境配置参数
const prodConfig = require("../config").build;
//引入webpack基本配置
const baseConf = require("./webpack.base.conf");
//一个创建html入口文件的webpack插件!
const HtmlWebpackPlugin = require("html-webpack-plugin");
//一个抽离出css的webpack插件!
const ExtractTextPlugin = require("extract-text-webpack-plugin");
//一个压缩css的webpack插件!
const OptimizeCSSPlugin = require("optimize-css-assets-webpack-plugin");
//一个拷贝文件的webpack插件!
const CopyWebpackPlugin = require("copy-webpack-plugin");

//资源路径
function assetsPath(_path) {
  return path.join(prodConfig.staticPath, _path);
}
//将webpack基本配置与生产环境配置合并!
const prodConf = merge(baseConf, {
  //项目出口配置
  output: {
    //Build后所有文件存放的位置
    path: path.resolve(__dirname, "../public"),
    //html引用资源路径,可在此配置cdn引用地址!
    publicPath: prodConfig.publicPath,
    //文件名
    filename: assetsPath("js/[name].[chunkhash].js"),
    //用于打包require.ensure(代码分割)方法中引入的模块
    chunkFilename: assetsPath("js/[name].[chunkhash].js")
  },
  //生成sourceMaps(方便调试)
  devtool: prodConfig.devtoolType,
  module: {
    //处理模块的规则(可在此处使用不同的loader来处理模块!)
    rules: [
      //使用vue-loader处理以vue结尾的文件!
      {
        test: /\.vue$/,
        loader: "vue-loader",
        options: prodConfig.vueloaderConf
      },
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          use: ["css-loader", "postcss-loader"],
          fallback: "vue-style-loader"
        })
      },
      {
        test: /\.less$/,
        use: ExtractTextPlugin.extract({
          use: ["css-loader", "less-loader", "postcss-loader"],
          fallback: "vue-style-loader"
        })
      }
    ]
  },
  plugins: [
    //每个chunk头部添加hey,xc-cli!
    new webpack.BannerPlugin("hey,xc-cli"),

    //压缩js
    new webpack.optimize.UglifyJsPlugin({
      parallel: true,
      compress: {
        warnings: false
      }
    }),

    //分离入口引用的css,不内嵌到js bundle中!

    new ExtractTextPlugin({
      filename: assetsPath("css/[name].[contenthash].css"),
      allChunks: false
    }),

    //压缩css
    new OptimizeCSSPlugin(),

    //根据模块相对路径生成四位数hash值作为模块id
    new webpack.HashedModuleIdsPlugin(),

    //作用域提升,提升代码在浏览器执行速度
    new webpack.optimize.ModuleConcatenationPlugin(),

    //抽离公共模块,合成一个chunk,在最开始加载一次,便缓存使用,用于提升速度!

    // 1. 第三方库chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: "vendor",
      minChunks: function(module) {
        //在node_modules的js文件!
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(path.join(__dirname, "../node_modules")) === 0
        );
      }
    }),

    // 2. 缓存chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: "manifest",
      minChunks: Infinity
    }),
    // 3.异步 公共chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: "app",
      children: true,
      // (选择所有被选 chunks 的子 chunks)
      async: true,
      // (创建一个异步 公共chunk)
      minChunks: 3
      // (在提取之前需要至少三个子 chunk 共享这个模块)
    }),

    //将整个文件复制到构建输出指定目录下
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, "../static"),
        to: prodConfig.staticPath,
        ignore: [".*"]
      }
    ]),

    //生成html
    new HtmlWebpackPlugin({
      filename: path.resolve(__dirname, "../public/index.html"),
      template: "index.html",
      favicon: path.resolve(__dirname, "../favicon.ico"),
      //js资源插入位置,true表示插入到body元素底部
      inject: true,
      //压缩配置
      minify: {
        //删除Html注释
        removeComments: true,
        //去除空格
        collapseWhitespace: true,
        //去除属性引号
        removeAttributeQuotes: true
      },
      //根据依赖引入chunk
      chunksSortMode: "dependency"
    })
  ]
});
module.exports = prodConf;

10. 创建 build/build.js

10.1 此文件是项目打包服务,用来构建一个全量压缩包
10.2

"use strict";
//node for loading
const ora = require("ora");
// rm-rf for node
const rm = require("rimraf");
//console for node
const chalk = require("chalk");
//path for node
const path = require("path");
//webpack
const webpack = require("webpack");
//webpack production setting
const config = require("./webpack.prod.conf");
//指定删除的文件
const rmFile = path.resolve(__dirname, "../public/static");
//build start loading
const spinner = ora("building for production...");
spinner.start();

//构建全量压缩包!
rm(rmFile, function(err) {
  if (err) throw err;
  webpack(config, function(err, stats) {
    spinner.stop();
    if (err) throw err;
    process.stdout.write(
      stats.toString({
        colors: true,
        modules: false,
        children: false,
        chunks: false,
        chunkModules: false
      }) + "\n\n"
    );

    if (stats.hasErrors()) {
      console.log(chalk.red("  Build failed with errors.\n"));
      process.exit(1);
    }

    console.log(chalk.cyan("  Build complete.\n"));
    console.log(
      chalk.yellow(
        "  Tip: built files are meant to be served over an HTTP server.\n" +
          "  Opening index.html over file:// won't work.\n"
      )
    );
  });
});

10.3 创建好以上文件 我们就可以通过npm run build来打包我们的项目文件并部署上线啦。

11.大功告成!

通过以上步骤,一个spa版的vue脚手架就大功告成啦!

如果对一些细节不懂的可以留言或者上我的github查看

https://github.com/webfansplz...

最后还是那句话,如果有帮助到你,请给我star支持哈!

查看原文

shinn_lancelot 收藏了文章 · 2019-07-24

gulp4.0升级小记

前言

周日在公司的新电脑在以前gulp3.9配置的目录按下npm install时发现报了错,百度了一下得知原来gulp已经到了4.0版本,就花了一点时间去升了个级,顺便记下我个人使用到的配置文件新版本的不同点,文笔和水平有限,多多见谅

1. 新Api引入

// v3.9
let gulp = require('gulp');

// v4.0
const { series, src, dest, watch } = require('gulp');

// 新引入的src,dest可替换老版的gulp.src和gulp.dest,代码更简洁
// watch是任务监听
// series是任务按顺序执行

2. 新的创建任务方式

// 下面以压缩图片插件 gulp-imagemin 为例

let imagemin = require('gulp-imagemin');

// v3.9
gulp.task('imagemin', () => {
  gulp.src('/path')
    .pipe(imagemin())
    .pipe(gulp.dest('/path'))
})

// 4.0
function minImage() {
    return src('/path')
            .pipe(imagemin())
            .pipe(dest('/path'))
}

// 新版本使用了函数和return进行任务设置,函数名不能和引入的插件变量名称重复

3. 执行任务方式

// v3.9
gulp.task('default', [task1, task2])

// v4.0,taskFn是设置任务的函数名
function defaultTask() {
    return series(taskFn1, taskFn2, taskFn3);  // series让任务按顺序执行
}
export.default = defaultTask() // 输出控制台执行任务的名称

// 新版本的export.xxxx,这个xxxx就是在控制台中gulp执行任务的名称,可以同时export设置多个任务
// 例如export.dev= dev(),想执行dev函数中返回的任务就在控制台输入gulp dev加回车!,如果是export.build = build(),则在控制台输入gulp build加回车!,如果是export.default = default(),直接输入gulp回车即可,以此类推

4. watch和series Api

// v3.9,老版本好像要安装一个queue的插件才可以实现按顺序执行任务
let watch = require('gulp-watch');
gulp.task('watch', () => {
  gulp.watch(['filePath1', 'filePath2'], [task1, task2]);
});

// 4.0
const { watch, series} = require('gulp');
function watchTask() {
    // 注意这里不需要使用return
    watch(['filePath1', 'filePath2'], series(taskFn1, taskFn2, taskFn3));
}

// 新版本直接引入watch即可实现任务监听功能,不用安装插件
// series也可以配合watch按顺序执行设置的任务函数

5. 插件gulp-autoprefixer配置变化

// v3.9
.pipe(autoprefixer({
      browsers: ['last 2 versions'],
      cascade: false
}))

// v4.0,需要在package.json文件添加browserslist键名或者在根目录添加一个.browserslistrc文件进行gulp-autoprefixer配置
// 详情可以参考:https://github.com/browserslist/browserslist#queries
.pipe(autoprefixer())

// .browserslistrc文件
last 1 version
> 1%
maintained node versions
not dead

// package.json
"browserslist": [
   "last 1 version",
   "> 1%",
   "maintained node versions",
   "not dead"
]

其他的配置感觉新版本和老版本都是大同小异,暂时就是发现了这么多,亲测可用


后记

我是使用sass + gulp-autoprefixer进行开发的,无意发现当autoprefixer碰到-webkit-box-orient: vertical;时会自动把这个样式给删了 0.0,折腾了一番找到解决方法如下

 -webkit-line-clamp: 3;
/*! autoprefixer:innore next */  2020-02-26更新
-webkit-box-orient: vertical;
overflow: hidden;

2020-02-26更新:

1、修复gulp-px3rem(px2rem)插件生成的css文件名成为xxx.debug.css的问题

(1)找到node_modules依赖包文件夹中的gulp-px3rem文件夹
(2)进入文件夹后修改index.js中第46行和第60行的代码

46  path: file.path.replace(/(.debug)?.css$/, dpr + 'x.debug.css'), 改成 path: file.path.replace(/(.debug)?.css$/, dpr + '.css'),

60  path: file.path.replace(/(.debug)?.css$/, '.debug.css') 改成 path: file.path.replace(/(.debug)?.css$/, '.css')
(3)重启服务

2、关于使用了gulp-px3rem(px2rem)后,1px的border不能正常显示的解决方法,在css语句后添加/*no*/表示忽略该语句

.selector {
  border: 1px solid #fff;/*no*/
}

3、个人使用px2rem的方法

1、在gulefile.js的px2rem配置中设置remUnit为设计稿的宽度,默认为75
  px2rem({
    remUnit: 750 // remUnit表示rem转换比例,现在的值为750表示1rem=750px
  })
2、把html的字体大小设置成屏幕大小,下面是限制了页面的最大宽度
window.onload = function() {
  let windowWidth = document.documentElement.clientWidth;
  document.documentElement.style.fontSize = (windowWidth > 750 ? 750 : windowWidth) + 'px';
}
3、设计稿是多少px,CSS就多少px

下面是我个人的gulp4.0.2配置,希望多多指点!

查看原文

shinn_lancelot 收藏了文章 · 2019-07-15

从0搭建rollup+vue组件模板,轻松发布npm、githubpages

最近更新模板

vue-cli3携手rollup、github-actions打造自动部署的vue组件模板(使用篇)2020-01-08

前言

既然是rollup+vue组件模板,就不说明为什么采用这个模式来开发组件了。
需要了解rollup的看文档:rollup官方文档

rollup打包

找个文件夹,开干

mkdir vue-rollup-component-template
cd vue-rollup-component-template
npm init

安装 rollup

npm i -D rollup

配置文件 rollup.config.js

export default {
  input: 'src/index.js',
  output: {
    name: 'vue-rollup-component-template',
    file: 'dist/vue-rollup-component-template.js',
    format: 'umd'
  }
}

入口文件 src/index.js

const x = 1;

export default (y) => x + y

修改 package.json 命令

"scripts": {
    "build": "rollup --config rollup.config.js"
}

打包

npm run build

完成了一个简单的打包。

不同打包格式与命令

"scripts": {
    "build": "npm run build:browser && npm run build:es && npm run build:umd",
    "build:browser": "rollup --config build/rollup.config.browser.js",
    "build:es": "rollup --config build/rollup.config.es.js",
    "build:umd": "rollup --config build/rollup.config.umd.js"
},

通用配置 build/rollup.config.base.js

export default {
  input: 'src/index.js',  //入口
  plugins: [] // 插件
}
  • es – ES模块文件。

安装js压缩插件

npm i -D rollup-plugin-uglify-es

build/rollup.config.es.js

import base from './rollup.config.base'
import uglify from 'rollup-plugin-uglify-es' //js压缩

const config = Object.assign({}, base, {
  output: {
    exports: 'named',
    name: 'vue-rollup-component-template',
    file: 'dist/vue-rollup-component-template.min.js',
    format: 'iife'
  },
})

config.plugins.push(uglify())

export default config
  • umd – 通用模块定义,以amd,cjs 和 iife 为一体。

build/rollup.config.browser.js

import base from './rollup.config.base'

const config = Object.assign({}, base, {
  output: {
    exports: 'named',
    name: 'vue-rollup-component-template',
    file: 'dist/vue-rollup-component-template.umd.js',
    format: 'umd'
  },
})

export default config
  • iife – 一个自动执行的功能,适合作为< script >标签。

build/rollup.config.browser.js

import base from './rollup.config.base'
import uglify from 'rollup-plugin-uglify-es' //js压缩

const config = Object.assign({}, base, {
  output: {
    exports: 'named',
    name: 'VueRollupComponentTemplate',
    file: 'dist/vue-rollup-component-template.min.js',
    format: 'iife'
  },
})

config.plugins.push(uglify())

export default config

打包

npm run build

安装插件

组件开发少不了引入模块、es6等,需要用 插件(plugins) 在打包的关键过程中更改 Rollup 的行为。

npm i -D rollup-plugin-node-resolve rollup-plugin-commonjs
npm i -D  rollup-plugin-vue vue vue-template-compiler

修改build/rollup.config.base.js

import resolve from 'rollup-plugin-node-resolve' // 告诉 Rollup 如何查找外部模块
import commonjs from 'rollup-plugin-commonjs' // 将CommonJS模块转换为 ES2015 供 Rollup 处理
import vue from 'rollup-plugin-vue' // 处理vue文件
import babel from 'rollup-plugin-babel'  // rollup 的 babel 插件,ES6转ES5

export default {
  input: 'src/index.js',
  plugins: [
    resolve({ extensions: ['.vue'] }),
    commonjs(),
    vue(),
    babel()
  ]
}
rollup-plugin-babel 插件需要 babel 支持
npm i -D babel-core babel-preset-env babel-preset-stage-3 rollup-plugin-babel@3.0.0

新建 .babelrc

{
  "presets": [
    ["env", { "modules": false }],
    "stage-3"
  ],
}

组件开发与root设置

设置 root

npm i -D babel-plugin-module-resolver

.babelrc 增加 plugins

{
  "plugins": [
    [
      "module-resolver",
      {
        "root": ["src/"]
      }
    ]
  ]
}

从而src目录下的模块引入,不需要'../../',只要 (年龄小的、身材好的)

import VList from 'components/list'
import util from 'utils/util'
import mixins from 'mixins/mixin'
...

组件开发

src/components/list/main.vue

<template>
  <div class="v-list">
    <slot/>
    <div class="v-list-date">
      <div class="v-list-date-label">当前时间:</div>
      <div class="v-list-date-text">{{date}}</div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'VList',
  data() {
    return {
      date: new Date()
    }
  }
}
</script>

src/components/list/index.js

import Main from './main';
export default Main

src/index.js

import VList from 'components/list';

const components = [VList]

const install = function (Vue) {
  components.forEach(component => {
    Vue.component(component.name, component)
  })
}

// 自动注册组件
if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue);
}

export default install

css打包压缩

npm i -D rollup-plugin-css-only  clean-css

build/rollup.config.base.js 增加

...
import css from 'rollup-plugin-css-only' // 提取css,压缩能力不行
import CleanCSS from 'clean-css' // 压缩css
import { writeFileSync } from 'fs' // 写文件

export default {
  input: 'src/index.js',
  plugins: [
    ...
    css({ output(style) {
      // 压缩 css 写入 dist/vue-rollup-component-template.css
      writeFileSync('dist/vue-rollup-component-template.css', new CleanCSS().minify(style).styles)
    } }),
    // css: false 将<style>块转换为导入语句,rollup-plugin-css-only可以提取.vue文件中的样式
    vue({ css: false }), 
    ...
  ]
}

rollup-plugin-css-only 支持 css及scss

src/components/list/main.vue

<style>
.v-list {
  width: 300px;
  margin: 0 auto;
}
</style>

<style lang="scss">
.v-list-date {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 30px;
  .v-list-date-label{
    font-size: 12px;
    color: #999;
  }
  .v-list-date-text{
    font-size: 12px;
    color: #666;
  }
}
</style>

<style scoped>
.v-list-1 {
  background-color: #333;
}
</style>

看起来一切顺利就可以打包发布npm了。

可能发了一堆bug到npm...,还没测试呢。

测试与docs

创建docs

其实就是 vue 的简易模板

vue init webpack-simple docs

安装依赖

cd docs
npm i

docs/src/main.js 引入、注册组件及css

...
import VueRollupComponentTemplate from '../../'
import '../../dist/vue-rollup-component-template.css'

Vue.use(VueRollupComponent)
...

docs/src/app.vue 使用组件

<template>
  <div id="app">
    <h1>vue-rollup-component-template</h1>
    <p>轻松进行组件开发、发布、展示</p>
    <v-list></v-list>
  </div>
</template>

docs/index.html js引入路径改为相对路径(加个点)

<script data-original="./dist/build.js"></script>

docs/.gitignore 把dist/忽略去掉,github展示页面需要访问dist/

...
dist/ 去掉这个
...
docs目录下只用改这几个地方就可以

准备测试

根目录下

npm i -D cross-env

package.json 添加命令

"scripts": {
    ...
    "dev": "cross-env NODE_ENV=development rollup --config build/rollup.config.es.js --watch"
},

package.json 修改/添加程序入口

...
"main": "dist/vue-rollup-component-template.umd.js",
"module": "dist/vue-rollup-component-template.esm.js",
"unpkg": "dist/vue-rollup-component-template.min.js",
...

开始测试

  • 根目录下
npm run dev
  • docs目录下
npm run dev
根目录src下内容修改会重新打包到dist,docs目录监听到dist目录变动会实时响应并刷新页面。

测试过后没问题就可以打包项目,登录npm发布组件了。

发布npm与githubpages

组件与测试是独立的。

  • 根目录下打包的组件可以直接发布npm,不用考虑测试生产环境,从而专注组件开发。
  • docs录下,在提交github之前打包,然后整个项目上传github。

发布npm

新建 .npmignore 文件,添加 npm 忽略文件

docs/
.babelrc

登录npm,然后发布

npm publish

githubpages

新建 .gitignore 文件,添加 git 忽略文件

.DS_Store
node_modules/
.vscode/
npm-debug.log
yarn-error.log

然后上传到github,打开项目设置,github pages的surce项选择docs。

打开分配的地址就可以观光页面了。

项目 github 地址

master分支是webpack3+babek6版本,如果需要webpck4+babel7版本请在 clone项目后切换 webpck4-babel7 分支
查看原文

shinn_lancelot 收藏了文章 · 2019-06-19

PHP极其强大的图片处理库Grafika详细教程(1):图像基本处理

Grafika是一个PHP图像处理库,是基于Imagick和GD,可以用于改变图片大小,剪裁,比较,添加水印等等功能。还有感知哈希,高级图像过滤,绘制贝塞尔曲线等功能,可谓非常强大。

由于功能太多,所以分成几篇文章写。

《1、图像基本处理》
《2、图像特效处理模块》
《3、图像属性处理》
《4、图形绘制》

优点:

  • 缩略图的速度非常快,质量非常高
  • 支持智能剪裁
  • 很好的支持GIF图片
  • 5种缩略图模式
  • 图像对比功能
  • 图像高级过滤功能
  • 图像混合
  • 其他图像处理库支持的API基本都支持

安装

下载

1、直接下载:

Grafika的官网Github地址

2、composer

    composer require kosinix/grafika:dev-master --prefer-dist

环境需求

  • PHP >= 5.3,当然官方推荐php7
  • GD库 >= 2.0版本
  • Imagick最好(不强求)>=3.3.0 , ImageMagick >= 6.5.3

部署

下载下来的Grafika目录基本结构像下面这样:

Grafika目录基本结构

不过composer下载下来的多一点儿,你只需要使用kosinix/grafika目录下的东西就好了。

我们在grafika目录下建立一个index.php,之后的操作都在这里。

grafika给我们提供了一个非常好用的autoloader.php位于src目录下。

index.php中引入它,(说明下,以下示例都需要引入这个autoloader.php文件,我们默认省略),下面就可以直接开发了。

require_once 'src/autoloader.php';

创建Editors

1、createEditor

grafika通过静态方法createEditor来创建一个editor。它包含所有的图片处理方法。

由于,grafika是基于ImagickGD库,所以使用createEditor方法会根据当前情况,自动选择所需要的图片处理库。(推荐使用)

use Grafika\Grafika; // Import package
$editor = Grafika::createEditor(); // Create the best available editor

2、Imagick Editor

当然你也可以直接使用Imagick类库。

use Grafika\Imagick\Editor; // Import package
$editor = new Editor(); // Imagick editor

注意:有些情况可能不支持该类库,你需要使用下面语句检查后使用,(不过你最好直接使用方法1,就没这些事)

use Grafika\Imagick\Editor; // Import package
$editor = new Editor(); // Imagick editor
if( $editor->isAvailable() ) { // Safety check

    // Your code here

}

3、GD Editor

你也可以直接使用GD库,也有些情况可能不支持,记得检查

use Grafika\Gd\Editor; // Import package
$editor = new Editor(); // Gd editor
if( $editor->isAvailable() ) { // Safety check

    // Your code here

}

创建图像

grafika允许你使用4种方式创建一个待处理的图像

1、直接打开图像

创建editor + open方法

use Grafika\Grafika;
$editor = Grafika::createEditor();
$editor->open( $image, 'path/to/image.jpg');

2、使用静态方法打开图片

使用直接打开、创建图片

use Grafika\Grafika;
$image = Grafika::createImage('path/to/image.jpg');

// 这里省略了$editor = Grafika::createEditor();

3、创建一个空白的画布

新建一个画布作为新图像

use Grafika\Grafika;
$image = Grafika::createBlankImage(100,100);

4、从已有图片拷贝一个

拷贝一个图像作为图像处理

$copy = clone $image;

这种方法你要保证之前有一张图片

这几种方法之后的操作大同小异,我们只选择第一种常规方法作为讲解示例

图片缩略图

我们先准备一个原图

图片描述

接下来,假设我们要创建的缩略图长:200px宽200px

1、Resize Fit

等比例缩放类型。那么就保证图片较长的一边不超过200px,等比缩放,缩放后不填充背景

use Grafika\Grafika;
$editor = Grafika::createEditor();
$editor->open($image1 , 'yanying.jpg'); // 打开yanying.jpg并且存放到$image1
$editor->resizeFit($image1 , 200 , 200);
$editor->save($image1 , 'yanying1.jpg');

$editor->open($image2 , 'yanying-h.jpg'); // 打开yanying.jpg并且存放到$image2
$editor->resizeFit($image2 , 200 , 200);
$editor->save($image2 , 'yanying2.jpg');

当然不要忘了第一行的require

图片描述图片描述

2、Resize Exact

固定尺寸缩放类型。就是不管图片长宽比,全部缩小到200px,可能导致图片变形。

use Grafika\Grafika;
$editor = Grafika::createEditor();
$editor->open($image1 , 'yanying.jpg'); // 打开yanying.jpg并且存放到$image1
$editor->resizeExact($image1 , 200 , 200);
$editor->save($image1 , 'yanying1.jpg');

$editor->open($image2 , 'yanying-h.jpg'); // 打开yanying.jpg并且存放到$image2
$editor->resizeExact($image2 , 200 , 200);
$editor->save($image2 , 'yanying2.jpg');

图片描述图片描述

3、Resize Fill

居中剪裁。就是把较短的变缩放到200px,然后将长边的大于200px的部分居中剪裁掉,图片不会变形。

use Grafika\Grafika;
$editor = Grafika::createEditor();
$editor->open($image1 , 'yanying.jpg'); // 打开yanying.jpg并且存放到$image1
$editor->resizeFill($image1 , 200,200);
$editor->save($image1 , 'yanying1.jpg');

$editor->open($image2 , 'yanying-h.jpg'); // 打开yanying.jpg并且存放到$image2
$editor->resizeFill($image2 , 200,200);
$editor->save($image2 , 'yanying2.jpg');

图片描述图片描述

4、Resize Exact Width

等宽缩放。和第一种功能相似,最终宽为200px,等比缩放,高度不管。

use Grafika\Grafika;
$editor = Grafika::createEditor();
$editor->open($image1 , 'yanying.jpg'); // 打开yanying.jpg并且存放到$image1
$editor->resizeExactWidth($image1 , 200);
$editor->save($image1 , 'yanying1.jpg');

$editor->open($image2 , 'yanying-h.jpg'); // 打开yanying.jpg并且存放到$image2
$editor->resizeExactWidth($image2 , 200);
$editor->save($image2 , 'yanying2.jpg');

图片描述

图片描述

5、Resize Exact Height

等高缩放。最终高为200px,等比缩放,不考虑图片宽度。

图片描述图片描述

图像对比功能

1、图片相似度对比

我们首先准备一张基本图,用来和其他图片对比。(segmentfault网页图片可能处理过,直接使用本文图片可能结果不一致)

图片描述

1、我们第一次使用一张灰度图片来比较

图片描述

use Grafika\Grafika;
$editor = Grafika::createEditor();
$result = $editor->compare('yanying.jpg' , 'yanying_grey.jpg');
var_dump($result); // int 2

说明: grafika图片对比方法compare返回一个数字,其中如果数字越接近于0,那么表示图片越相似。如果数字在0-10范围内,那么图片都可能相似。但是如果数字大于10,那么,可能就完全不同。

这里返回2,说明相似度还是非常高的。

2、我们再用一张缩小的图片来测试,记住都是和第一张基本图比较。

图片描述

use Grafika\Grafika;
$editor = Grafika::createEditor();
$result = $editor->compare('yanying.jpg' , 'yanying-smaller.jpg');
var_dump($result); // int 0

这里结果返回0,相似度非常高。

3、我们再用一张剪裁下来的局部图片测试

图片描述

use Grafika\Grafika;
$editor = Grafika::createEditor();
$result = $editor->compare('yanying.jpg' , 'yanying-half.jpg');
var_dump($result); // int 20

结果超过10了,相似度不怎么高

4、我们再用一张完全不同的图片测试

图片描述

use Grafika\Grafika;
$editor = Grafika::createEditor();
$result = $editor->compare('yanying.jpg' , 'yanying-h.jpg');
var_dump($result); // int 39

结果39,越来越大,越来越不像

2、比较图片是否相同

grafika提供方法equal来检查两张图片是否完全相同。这里的检查是一个像素一个像素的检测,所以时间可能会较长。

当然grafika也会预检查,如果两张图片大小不相同,则直接返回false。只有其他都相同后才会进行逐像素检查。

我们这里对比之前创建的一张缩略图,因为大小不一致,所以直接返回false

图片描述

use Grafika\Grafika;
$editor = Grafika::createEditor();
$result = $editor->equal('yanying.jpg' , 'yanying-smaller.jpg');
var_dump($result); // boolean false

智能剪裁

智能剪裁是自动识别图像中的重要部分,剪裁时候偏向于保留重点部分。

不过grafika也提供了人为操控位置剪裁,我们先说这个。

基本位置剪裁

基本位置剪裁包含9个位置

  • top-left
  • top-center
  • top-right
  • center-left
  • center
  • center-right
  • bottom-left
  • bottom-center
  • bottom-right

我们这里一起说了,这里我们使用900*600的图片,分成9块

图片描述

use Grafika\Grafika;
$editor = Grafika::createEditor();

$src = 'yanying.jpg';
$editor->open( $image, $src );
$editor->crop( $image, 300, 200, 'top-left' );
$editor->save( $image, 'result1.jpg' );
$editor->free( $image );

$editor->open( $image, $src );
$editor->crop( $image, 300, 200, 'top-center' );
$editor->save( $image, 'result2.jpg' );
$editor->free( $image );

$editor->open( $image, $src );
$editor->crop( $image, 300, 200, 'top-right' );
$editor->save( $image, 'result3.jpg' );
$editor->free( $image );

$editor->open( $image, $src );
$editor->crop( $image, 300, 200, 'center-left' );
$editor->save( $image, 'result4.jpg' );
$editor->free( $image );

$editor->open( $image, $src );
$editor->crop( $image, 300, 200, 'center' );
$editor->save( $image, 'result5.jpg' );
$editor->free( $image );

$editor->open( $image, $src );
$editor->crop( $image, 300, 200, 'center-right' );
$editor->save( $image, 'result6.jpg' );
$editor->free( $image );

$editor->open( $image, $src );
$editor->crop( $image, 300, 200, 'bottom-left' );
$editor->save( $image, 'result7.jpg' );
$editor->free( $image );

$editor->open( $image, $src );
$editor->crop( $image, 300, 200, 'bottom-center' );
$editor->save( $image, 'result8.jpg' );
$editor->free( $image );

$editor->open( $image, $src );
$editor->crop( $image, 300, 200, 'bottom-right' );
$editor->save( $image, 'result9.jpg' );
$editor->free( $image );

看下结果

图片描述

智能剪裁

原图

图片描述

我们使用智能剪裁将图片剪裁至200*200px

use Grafika\Grafika;
$editor = Grafika::createEditor();
$editor->open( $image, 'yanying-smaller.jpg' );
$editor->crop( $image, 200, 200, 'smart' );
$editor->save( $image, 'yanying-smart.jpg' );

发现还是可以突出重点的

图片描述

GIF缩略图

压缩GIF,不丢失动画

grafika可以直接压缩GIF图片,并且不丢失动画功能。

图片描述

use Grafika\Grafika;
$editor = Grafika::createEditor();
$editor->open( $image, 'sample.gif' );
$editor->resizeFit( $image, 250, 128 );
$editor->save( $image, 'output.gif' );

我们这里将原图压缩到原来的一半,发现动画并没有丢失

图片描述

移除GIF动画效果

当然,如果有需要,我们也可以直接移除GIF的动画效果

use Grafika\Grafika;
$editor = Grafika::createEditor();
$editor->open( $image, 'sample.gif' );
$editor->flatten( $image );
$editor->save( $image, 'output-no-animation.gif' );

图片描述

图片合并

图片合并需要2张图片,将其中一张作为基本图,准备的第二章图片就是放置在基础图片之上。

我们首先来看代码

use Grafika\Grafika;
$editor = Grafika::createEditor();
$editor->open($image1 , 'yanying-h.jpg');
$editor->open($image2 , 'yanying-smaller.jpg');
$editor->blend ( $image1, $image2 , 'normal', 0.9, 'center');
$editor->save($image1,'333/yanying-blend.jpg');

解释一下

首先打开两张图片,其中$image1为基础图片,也就是放在下面的。重点在blend这个方法。

其中

  • 第一个参数为基础图片
  • 第二个参数为放置在基础图片之上的图片normal, multiply, overlay or screen.,这里的类型意思就是图片叠加的模式,下面会给出实例看每种的不同。
  • 第三个参数为透明度,这个不说太多,容易想到。
  • 第四个为位置,有10个选择,其中,前面9种为用户自定义拜访位置,而最后一个是智能拜访,由grafika来判断摆放在哪里好。 top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right and smart
  • 第五个参数为可选参数,表示图片2距离图片1左边的距离
  • 第六个参数也为可选参数,表示图片2距离图片1上边的距离

我们试着摆几种情况。

1、normal

其中位置信息:center,透明度为0.9,也就是上面代码的那种

图片描述

2、multiply

位置信息:,top-left,其他不变

图片描述

3、overlay

位置信息:bottom-right,其他不变

图片描述

4、screen

位置信息:,最后一个位置参数不给,也就是默认top-left

图片描述

图像旋转

图像旋转比较简单,只需要给一个旋转角度参数就可以了,如果想要给背景填充个颜色,再给一个颜色参数即可。(默认不给背景色为黑色)

代码如下

use Grafika\Grafika;
use Grafika\Color;
$editor = Grafika::createEditor();
$editor->open($image , 'yanying-smaller.jpg');
$editor->rotate($image ,'45',new Color('#ff0000'));
$editor->save($image,'333/yanying-rotate.jpg');

最后一个背景颜色参数也是需要Color对象

图片描述

图片写文字

在图片上面写文字的参数比较多,不过如果正常使用,只需要给前两个必填的即可,后面的参数都是可选的。

我们逐一的来看各个参数

  • image:所需要写文字的图片
  • text:需要写的文字
  • size:(选填)字体大小,默认为12px
  • x:(选填)文字的最左边距离图片最左边的距离,默认为0
  • y:(选填)文字的基线到图片的最上边的距离,默认是12px,也就是文字的高度。(基线你就当做文字最下面好了)
  • color:(选填)字体颜色,Color对象,需要new Color一下,默认为黑色。
  • font:(选填)字体的完整路径,默认Sans font.
  • angle:(选填)文字旋转角度,取值范围为0-359,默认为0,也就是不旋转

我们随便找个文字试试

use Grafika\Grafika;
use Grafika\Color;
$editor = Grafika::createEditor();
$editor->open($image , 'yanying-smaller.jpg');
$editor->text($image ,'yanying',30,200,100,new Color("#000000"),'',45);
$editor->save($image,'333/yanying-text.jpg');

看下效果。这里说明下,如果文字为中文,需要找一个支持中文的字体。默认字体不支持中文,所以你写中文,就是都是小方框。

图片描述


严颖,PHP研发工程师

2016-11-07日晚

博客:segmentfault主页

推荐一个我们团队自己开发的针对开发者的网址导航:笔点导航 - 用心做最简洁的网址导航

可以自定义网址
可以自定义分类
分类可以标记颜色
自定义皮肤
自定义搜索
网址拖拽排序
自定义插件小模块

图片描述

查看原文

shinn_lancelot 收藏了文章 · 2019-05-29

Canvas 在高清屏下绘制图片变模糊的解决方法

之前我在 SF 上回答过「html5 canvas绘制图片模糊的问题」,但是可能是由于我给出的答案过于简略,加上答案中的 demo 链接已经失效,很多人反映这种办法并不好使。但是我在给出答案之前是在小米2 和 iPhone 上测试过的,没有任何问题。下面我会一步一步地描述具体的步骤。

前提条件

假设我们要在 canvas 中绘制一张 300 x 90 的图片,并且要保证它在高清屏中不模糊。那么我们首先要准备一张 600 x 180 的图片,处理过高清屏的同学应该会有这方面的经验。

问题重现

OK,我们先把问题重现一下,以便有一个更直观的了解。下面是相关的代码(为了简单起见,我把代码都写在了 HTML 文档里面):

<!-- 通过 img 标签引入图片,以便绘制到 canvas 中 -->
<img data-original="html5rocks.png" alt="" width="300" height="90">

<!-- canvas -->
<canvas width="300" height="90"></canvas>

<script>
    function init() {
        var canvas = document.querySelector('canvas');
        var ctx = canvas.getContext('2d');
        ctx.drawImage(document.querySelector('img'), 0, 0, 300, 90);
    }
    window.onload = init;
</script>

代码很简单,没有做任何处理,具体的效果和完整的代码可以查看这个 DEMO,这个 demo 在高清屏的手机中会出现的问题:canvas 中的图片变模糊了!。

至于为什么会变模糊,这和浏览器处理 canvas 的方式有关,相关的文章可以参考这篇 High DPI Canvas,这里不作深入介绍。

解决问题

首先,引入 hidpi-canvas-polyfill

其实,不只是绘制图片时会出现模糊的问题,正常情况下,在高清屏的设备中,任何绘制在 canvas 中的图形(包括文字)都会出现模糊的问题。上面这个 polyfill 就是为了解决这个问题,但是它没有处理图片。

接下来,修改绘制图片的代码

init 函数修改成下面这样:

function init() {
    var canvas = document.querySelector('canvas');
    var ctx = canvas.getContext('2d');

    // polyfill 提供了这个方法用来获取设备的 pixel ratio
    var getPixelRatio = function(context) {
        var backingStore = context.backingStorePixelRatio ||
            context.webkitBackingStorePixelRatio ||
            context.mozBackingStorePixelRatio ||
            context.msBackingStorePixelRatio ||
            context.oBackingStorePixelRatio ||
            context.backingStorePixelRatio || 1;
    
        return (window.devicePixelRatio || 1) / backingStore;
    };

    var ratio = getPixelRatio(ctx);
    
    // 注意,这里的 width 和 height 变成了 width * ratio 和 height * ratio
    ctx.drawImage(document.querySelector('img'), 0, 0, 300 * ratio, 90 * ratio);
}

可以点击这里查看完整的代码和效果,在高清屏的设备中打开,看看 cavans 中的图片是否完美显示。

说明

这个解决方案本质上和 @白一梓 的答案是一样的:先放大 canvas,再用 CSS 将其限制回原始大小。

如果你看了 polyfill 的代码就会明白其中的原理了,这个 polyfill 的代码十分简短明了,它做了两件事:一是将 canvas 的高和宽分别乘以 ratio 将其放大,然后又用 CSS 将高和宽限制成初始的大小;二是 hack canvas 中常用的函数,如:fillRect, clearRect, lineTo, arc 等,将它们的参数都乘以 ratio,以方便我们可以像以前那样使用这些方法,而不用在传参的时候手动乘以 ratio。

查看原文

shinn_lancelot 赞了文章 · 2019-04-09

电视机顶盒web开发总结,避免踩坑。

1.电视机顶盒web开发总结

针对东方有线机顶盒UUTVOS操作系统中内置的联彤浏览器web开发,总结一些自己在开发中遇到的问题和技巧。浏览器是基于Firefox的阉割版,所以开发中有一些莫名其妙的坑。已经尝试过使用Vue开发机顶盒web项目,体验较差:首次加载时间长、页面卡顿。由于项目进度推进,当时没有尝试组件懒加载和路由懒加载处理,这样做或许可以减少首次加载时间。推荐使用 JQuery 进行开发。

1.1采坑预告

1.2开发总结

1.2.1一个WebStrom就够了☞↑

我们的后台是现成的,直接把代码拷贝到服务器上,在机顶盒上就可以随时预览到项目。
  • WebStrom 的工具栏中的 Tools>Deployment 可以连接到配置远程服务器上,每次 CTRL + S 会自动上传项目文件,好用的不要不要的。墙裂推荐!
  • 通过配置 WebStrom,可以监听编译 Sass 文件,CTRL + S 自动编译就是这么方便。
  • 喜欢 VSCode 的话,未尝不可,或许 VSCode 里也有这些功能插件,我没去折腾罢了。
  • 如果在 WS 中使用了 Sass 或者 Less ,每次保存的时候,被编译后的 CSS 文件是不会自动上传到服务器上的,需要在 WS 里手动上传。

1.2.2用自己喜欢的技术☞↑

  • 机顶盒web开发官方文档推荐用原生 JS 开发,目前来看的话,JQ 用起来方便一些,暂时没有性能缺陷。
  • Less、Sass 两个都大爱。变量的威力大大的,就算美工切得是1080p机器的图,我拿来布局到720p上,利用 Sass 的变量和计算特性,非常容易控制CSS中的属性值。
  • 做列表渲染的时候用到了 art-template,腾讯出的一个模板引擎,参考它的文档,还是很容易上手的。官方文档

1.2.3少用JS控制呈现 HTML 元素☞↑

机顶盒浏览器的性能非常低,如果还要做视频播放的话,JS 可发挥的空间相当有限。
  • 一个 Tab 栏下有6个选项,选项里面 HTML 结构基本都是相同的,如果你打算用 JS 复用相同结构的 HTML 代码的话,赶紧停下,像我图片上这样老老实实的 copy 和 paste HTML代码吧。不然切换 Tab 的时候,随机的卡顿很恶心。
    clipboard.png
  • 类似 $(id).css({"backgroundImage":"url('...')"})$(id).attr({"src":"./*.jpg"}) 这样的在 JS 里面控制 UI 显示层面的操作要避免,尽量直接在 HTML 中完成,最多能接受这个操作: $(id).addClass()。机顶盒浏览器就是这么傲娇。(这是我试出来的,至于JQ操作性能方面的差异本质还是需要研究的。)
  • 机顶盒web中按钮的尺寸一般都很大,按钮背景图这些东西,就不要在 JS 中去操作,如果播放视频引起了性能高损耗,这个时候web中的UI卡的你一愣一愣的。

1.2.4“焦点事件”使用一时爽☞↑

  • 一定要避免使用"焦点事件"触发相关操作,焦点事件是高频率的系统事件,web在机顶盒运行时,焦点事件一般不受开发人员的绝对控制。“失去焦点”事件同样要避免使用。
  • “焦点事件”与“上下左右按键事件”具有一定的耦合性,“焦点事件”使用不当,问题百出。
  • 上下左右按键事件,一般都可以替换焦点事件。
  • a:focus {} 这个CSS选择器可以放心的使用。

1.2.5万能的 setTimeout() ☞↑

机顶盒内置的浏览器很恶心啊,阉割版的就算了,一些逻辑上的东西跟PC上也不同。
  • 一些操作无论怎么写都不运行,或者拿不到值(null),特别是在页面加载、父子页面跳转这些场景下。给它加个 setTimeout(function(), ms) 就搞定了,百试百灵,一般人我都不告诉他^_^。
$(document).keydown(function () {
    if (event.which  === 4097) {
        var distance = $("#list").scrollTop();
        sessionStorage.removeItem("listScrollTopVal");
        sessionStorage.setItem("listScrollTopVal", JSON.stringify(distance));
        // 按下确定键后,把获得焦点的元素的 id 保存到 sessionStorage中,
        // 这个时候就要在外边加一个延时函数,甚至可以将时间设置成 0ms 也行。
        setTimeout(function () {
            sessionStorage.removeItem("listFocusItemId");
            sessionStorage.setItem("listFocusItemId",JSON.stringify(document.activeElement.id));
        }, 100);
    }
});

1.2.6绝对定位position:absolte;省时省力☞↑

  • 机顶盒的可视区域是固定的,绝对定位是最省时省力的。
  • 拥有绝对定位元素的父元素必须是 position:relative定位,这个是必须的!
  • 多个块级元素排列在同一行,考虑使用display:inline-block;,优于使用flaot:...浮动布局。

1.2.7overflow:scroll;不能往上滚动☞↑

电视机的可视区域固定,整个页面是不滚动的,业务场景中,页面中的局部需要滚动:列表页、详情页。
  • 在PC上,给需要滚动的元素设置:overflow:scroll; 会出现滚动条,实现滚动。但是在电视机顶盒上,出现了:能往下滚动,不能往上滚动的问题。
  • 解决办法:给需要滚动的元素包裹一个 <a href="#"></a>并且必须设置display:block。;
<div class="content">
    <a href="#/" style="display:block;outline:none;">
      <div class="content-html">需要滚动的内容</div>
    </a>
  </div>

1.2.8切换视频播放,加防抖必不可少☞↑

机顶盒浏览器的性能本来就很差,在同一个页面的 Tab 上切换多个视频播放,按键过快的情况下,UI上焦点连续切换过去很多个元素了,视频的播放地址才挨个往过去切换,这个时候很容易造成卡顿或者浏览器假死。
  • 防抖其实就是一个延时函数,可以想象成:刷卡上公交车,只要有人刷卡,司机就不能开车。
$("#nav--second").keydown(function(event){
    if(event.which === 39) {
        // 这里的EVAN是一个全局的命名空间,EVAN.timer是一个全局变量
        clearTimeout(EVAN.timer);
        EVAN.timer = setTimeout(function () 
        create(EVAN.homePageVideoUrlArr[2]);
        }, EVAN.gap);  // 时间1-2s左右比较合适。
    }
});
查看原文

赞 12 收藏 9 评论 8

认证与成就

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

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-03-30
个人主页被 85 人浏览