被遗忘的 DOM

事件流

描述的是从页面中接受事件的顺序。

  • 事件捕获流
  • 事件冒泡流

事件冒泡:即事件最开始由最具体的元素(文档中嵌套层次最深的那个节点)接受,然后逐级向上传播至最不具体的那个节点(文档)。
事件捕获:不太具体的节点应该更早接受到事件,而最具体的节点最后接受到事件。

事件处理程序

  1. HTML 事件处理程序: button onclick=”showTextModel()”
  2. DOM0 级事件处理程序: var btn = document.getElementById(“btn”); btn.onclick = function(){} btn.onclick = null;//删除事件
  3. DOM2 级事件处理程序:addEventListener() 和 removeEventListener。参数:处理事件名、处理事件程序、布尔值。true 事件捕获时触发,false 事件冒泡时触发。
  4. IE 事件处理程序:attachEvent() 添加事件 detachEvent 删除事件 参数:处理史建明、处理事件程序。IE8 以及更早的浏览器版本只支持事件冒泡。
  5. 跨浏览器的事件处理程序
1
2
3
4
5
6
7
8
9
10
11
var eventUtil = {
addHandler: function(element, type , handler){
if(element.addEventlistener){
element.addEventlistene(type,handler,false);
}else if(element.attachEvent){
element.attachEvent('on'+type,handler);
}else{
element['on'+type] = handler
}
}
}

事件对象

在触发 DOM 上的事件时都会产生一个对象

事件对象 event
DOM 中的事件对象
type 属性:用于获取事件类型
target 属性:用于获取事件目标
offsetWidth 属性:水平方向 width + 左右padding + 左右border-width
clientWidth 属性:水平方向 width + 左右padding
stopPropagation()方法 用于阻止事件冒泡
preventDefault()方法 阻止事件的默认行为

IE 中的事件对象
type 属性:用于获取事件类型
srcElement属性:用于获取事件目标
window.event
cancelBubble 属性 用于阻止事件冒泡 true 阻止事件冒泡 false 不阻止事件冒泡
returnValue 属性 用于阻止事件的默认行为 true 阻止事件默认行为 false 不阻止事件默认行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
var eventUtil={
// 添加句柄
addHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent('on'+type,handler);
}else{
element['on'+type]=handler;
}
},
// 删除句柄
removeHandler:function(element,type,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent('on'+type,handler);
}else{
element['on'+type]=null;
}
},

getEvent:function(event){
return event?event:window.event;
},

getType:function(event){
return event.type;
},

getElement:function(event){
return event.target || event.srcElement;
},

preventDefault:function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue=false;
}
},

stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble=true;
}
}
}

事件类型

鼠标事件

  • click 点击
  • dblclick 双击
  • mouseover、mouseout 鼠标移入、移除
  • mouseenter、mouseleave 鼠标移入、移除

键盘事件

  • keyDown 按下键盘上任意键时触发
  • keyUp 释放键盘上的键时触发
  • keyPress 按下键盘上字符键时触发

节点

节点的类型

节点的类型

文档片段节点

1
2
3
4
5
var frag = document.createDocumentFragment();
var li = document.createElement("li");
li.innerHTML = "List item ";
frag.appendChild(li);
document.getElementById("list-node").appendChild(frag)

判断节点类型

1
2
3
4
5
6
7
8
var containerElement = document.getElementById("container");
containerElement.nodeType == Node.ELEMENT_NODE
containerElement.nodeType == 1
containerElement.nodeName == 'div'
containerElement.nodeValue == 'Falldown'
containerElement.attributes[0]
containerElement.childNodes[0]
document.doctype

元素节点的判断

isElement() 判定是否为元素节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var testDiv = document.createElement("div");
var isElement = function (obj) {
if (obj && obj.nodeType === 1) {
//先过滤最简单的
if (window.Node && obj instanceof Node) {
//如果是IE9,则判定其是否Node的实例
return true; //由于obj可能是来自另一个文档对象,因此不能轻易返回false
}
try {
//最后以这种效率非常差但肯定可行的方案进行判定
testDiv.appendChild(obj);
testDiv.removeChild(obj);
} catch (e) {
return false;
}
return true;
}
return false;
};
isElement(document.getElementByID("test"));

isHTML 判断是不是 HTML 节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var testDiv = document.createElement('div');
var isElement = function (obj) {
if (obj && obj.nodeType === 1) {//先过滤最简单的
if( window.Node && (obj instanceof Node )){ //如果是IE9,则判定其是否Node的实例
return true; //由于obj可能是来自另一个文档对象,因此不能轻易返回false
}
try {//最后以这种效率非常差但肯定可行的方案进行判定
testDiv.appendChild(obj);
testDiv.removeChild(obj);
} catch (e) {
return false;
}
return true;
}
return false;
}

var isHTMLElement(el){
if(isElement){
return isXML(el.ownerDocument);
}
return false;
}

isXML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//Sizzle, jQuery自带的选择器引擎
var isXML = function (elem) {
var documentElement =
elem && (elem.ownerDocument || elem).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
console.log(isXML(document.getElementById("test")));

//但这样不严谨,因为XML的根节点,也可能是HTML标签,比如这样创建一个XML文档
try {
var doc = document.implementation.createDocument(null, "HTML", null);
console.log(doc.documentElement);
console.log(isXML(doc));
} catch (e) {
console.log("不支持creatDocument方法");
}

//我们看看mootools的slick选择器引擎的源码:
var isXML = function (document) {
return (
!!document.xmlVersion || !!document.xml || toString.call(document) == "[object XMLDocument]" || (document.nodeType == 9 && document.documentElement.nodeName != "HTML"));
};

//精简版
var isXML = window.HTMLDocument
? function (doc) {
return !(doc instanceof HTMLDocument);
} : function (doc) {
return "selectNodes" in doc;
};

var isXML = function (doc) {
// 判断返回大小写区分 xml 和 html
return (doc.createElement("p").nodeName !== doc.createElement("P").nodeName);
};

contains 判断是否是其子节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 var pNode = document.getElementById("p-node");
var cNode = document.getElementById("c-node").childNodes[0];
alert(pNode.contains(cNode));

function fixContains(a, b) {
try {
while ((b = b.parentNode)){
if (b === a){
return true;
}
}
return false;
} catch (e) {
return false;
}
}
var pNode = document.getElementById("p-node");
var cNode = document.getElementById("c-node").childNodes[0];
alert(fixContains(pNode, cNode));

节点的 nodeName 与 nodeValue

浏览器渲染引擎的基本渲染流程

解析 HTML 构建 DOM 树(构建 DOM 节点) -> 构建渲染树(解析样式信息) -> 布局渲染树(布局 DOM 节点) -> 绘制渲染树(绘制 DOM 节点)

Webkit 主要渲染流程

domReady

1
window.onload

domReady 实现策略

在页面的 DOM 树创建完成后(也就是 HTML 解析第一步完成)即触发,而无需等待其他资源的加载。即 DOMReady 实现策略:
1、支持 DOMContentLoaded 事件的,就使用 DOMContentLoaded 事件
2、不支持的,就用来自 Diego Perini 发现的著名 Hack 兼容。兼容原理大概就是,通过 IE 中的 document.docuemntElement.doScroll(‘left’) 来判断 DOM 树是否创建完毕。

实现 domReady

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function myReady(fn){
//对于现代浏览器,对DOMContentLoaded事件的处理采用标准的事件绑定方式
if ( document.addEventListener ) {
document.addEventListener("DOMContentLoaded", fn, false);
} else {
IEContentLoaded(fn);
}

//IE模拟DOMContentLoaded
function IEContentLoaded (fn) {
var d = window.document;
var done = false;

//只执行一次用户的回调函数init()
var init = function () {
if (!done) {
done = true;
fn();
}
};

(function () {
try {
// DOM树未创建完之前调用doScroll会抛出错误
d.documentElement.doScroll('left');
} catch (e) {
//延迟再试一次~
setTimeout(arguments.callee, 50);
return;
}
// 没有错误就表示DOM树创建完毕,然后立马执行用户回调
init();
})();

//监听document的加载状态
d.onreadystatechange = function() {
// 如果用户是在domReady之后绑定的函数,就立马执行
if (d.readyState == 'complete') {
d.onreadystatechange = null;
init();
}
}
}
}

DOM 节点继承层次



HTML 嵌套规则

HTML存在许多种类型的标签,有的标签下面只允许特定的标签存在,这就叫做 HTML 嵌套规则。

不按 HTML 嵌套规则写,浏览器就不会正确解析,会将不符合嵌套规则的节点放到目标节点的下面,或者变成纯文本。

块状元素:一般是其他元素的容器,可容纳内联元素和其他块状元素,块状元素排斥其他元素与其位于同一行,宽读高度起作用。常见块状元素为 div 和 p。

内联元素:内联元素只能容纳文本或者其他内联元素,它允许其他内联元素与其位于同一行,但宽度高度不起作用。常见内联元素为 a。

块状元素与内联元素嵌套规则

1、块元素可以包含内联元素或某些块元素,但内联元素却不能包含块元素,它只能包含其他内联元素。
2、块元素不能放在 p 元素内。
3、有几个特殊的块级元素只能包含内联元素,不能包含块级元素。:h1 ~ h6
4、块级元素与块级元素并列、内联元素与内联元素并且




相关资料
[视频]DOM事件探秘
[视频]DOM探索之基础详解篇

盘点HTML字符串转DOM的各种方法及细节

javascript中的offsetWidth、clientWidth、innerWidth及相关属性方法
前端必读:浏览器内部工作原理
[转] 主流JS框架中DOMReady事件的实现
如何检查JavaScript对象是否为DOM对象?


相关代码
DOM

Vue Router

Vue Router

任何组件内通过 this.$router 访问路由器,可以通过 this.$route 访问当前路由

回退

1
2
3
goBack() {
window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
}

动态路由匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
{ path: '/user/:username/post/:post_id', component: User }
]
})

// 接受参数
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
this.$route.params.id
当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
1
2
3
4
5
6
7
8
9
10
watch: {
$route(to, from) {
// 对路由变化作出响应...
}
// 导航守卫
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
}

捕获所有路由

1
2
3
4
5
6
7
8
{
// 会匹配所有路径
path: '*'
}
{
// 会匹配以 `/user-` 开头的任意路径
path: '/user-*'
}

当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 参数。它包含了 URL 通过通配符被匹配的部分:

1
2
3
4
5
6
// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 给出一个路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'

路由嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<div id="app">
<router-view></router-view>
</div>

const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>`
}

const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
// 当 /user/:id 匹配成功,
// UserHome 会被渲染在 User 的 <router-view>
{ path: '', component: UserHome },

// ...其他子路由
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view>
path: 'profile',
component: UserProfile
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view>
path: 'posts',
component: UserPosts
}
]
}
]
})

编程式的导航

声明式 编程式
router-link :to=”…” router.push(…)
1
2
3
4
5
6
7
8
9
10
11
12
13
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user

后退或前进

1
2
3
4
5
6
7
8
9
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
// 前进 3 步记录
router.go(3)
// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)

命名路由

1
2
3
4
5
6
7
8
9
10
11
12
13
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>

router.push({ name: 'user', params: { userId: 123 }})

命名视图

1
2
3
4
5
6
<div>
<h1>User Settings</h1>
<NavBar/>
<router-view/>
<router-view name="helper"/>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
path: '/settings',
// 你也可以在顶级路由就配置命名视图
component: UserSettings,
children: [{
path: 'emails',
component: UserEmailsSubscriptions
}, {
path: 'profile',
components: {
default: UserProfile,
helper: UserProfilePreview
}
}]
}

重定向和别名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})

const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})

const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})

const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})

路由组件传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  const User = {
template: '<div>User {{ $route.params.id }}</div>'
}

const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})

// 通过 props 解耦

const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true },

// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})

Vue Router 传参

params 传值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User, name:'user',}
]
})

// 传值
this.$router.push({
name:"'user'",//这个name就是你刚刚配置在router里边的name
params:{
userId:"10011"
}
})

// 取值
this.$route.params.userId

query 传值

1
2
3
4
5
6
<template>
<router-link
:to="{ path: '/log', query: { name1: 'haha', name2: 'enen' } }"
>
</router-link>
</tempalte>
1
2
3
4
5
6
7
8
9
10
11
//  传值
this.$router.push({
path:"/user",//这个path就是你在router/index.js里边配置的路径
query:{
userId:"10011"
}
})

// 取值
this.$router.currentRoute.query.userId
this.$route.query.userId

基础路径

1
2
3
4
5
6
7
8
9
10
//  base 基础路径
const router = new VueRouter({
mode: 'history', // 只有 history 模式下 base 才起作用
base: '/app', // 基础路径 默认为 /
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
{ path: '/user/:username/post/:post_id', component: User }
]
})

hase 模式下 base 不起作用,可以用路由嵌套的方式加上前缀


相关资料

vue Router
vue $router 路由传参的4种方法详解
vue router跳转方法
vite —— 一种尤雨溪开发的新的、更快地 web 开发工具
vite

JavaScript 书写规范及技巧

强类型检查

使用 === 而不是 ==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0 == false // true 
0 === false // false
2 == "2" // true
2 === "2" // false

// example
const val = "123";
if (val === 123) {
console.log(val);
// it cannot not be reached
}
if (val === "123") {
console.log(val);
// it can be reached
}

变量命名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 不推荐
let daysSLV = 10;
let y = new Date().getFullYear();
let ok;
if (user.age > 30) {
ok = true;
}

// 推荐
const MAX_AGE = 30;
let daysSinceLastVisit = 10;
let currentYear = new Date().getFullYear();
...
const isUserOlderThanAllowed = user.age > MAX_AGE;

// 不要添加一些额外且不必要的词汇到你的变量名称中。

let nameValue;
let theProduct;

// 推荐
let name;
let product;

设置原型的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Person(name) {
this.name = name;
}


function Student(name) {
Person.call(this, name);
}

Student.prototype = Object.create(Person.prototype);
// Student 的原型会被设置为 Person。

// 实际上想要将 Student 的原型设置为 Student。
// 需要这么写
Student.prototype.constructor = Student;

// 或者
class Student extends Person {
}

创建 Web Workers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script id="worker" type="javascript/worker">
self.onmessage = (e) => {
self.postMessage('msg');
};
</script>
<script>
const blob = new Blob([
document.querySelector('#worker').textContent
]);

const worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = (e) => {
console.log(e.data);
}
worker.postMessage("hello");
</script>

map 技巧

map 里使用 parseInt 会返回 NaN

1
2
3
4
5
6
//  不要这么写
['1','2','3'].map(parseInt)

// 可以
['1','2','3'].map(Number)
['1','2','3'].map(num => parseInt(num, 10))

三目运算符

1
2
3
4
5
// 当 x === 1 返回 false 不想调用 doSomethingElse 时
x === 1 ? doSomething() : doSomethingElse();

// 可以
x === 1 && doSomething();

清除 Yarn 中的缓存

1
yarn cache clear

innerText 在 IE 有效,其他浏览器无效

1
2
const el = document.getElementById('foo');
el.textContent = 'foo';

[译] JavaScript 技巧 —— 子代构造函数,文本选择,内联 Workers 等等
【译】JS代码整洁之道——快速最佳实践
JavaScript闭包应用介绍
如何精确统计页面停留时长

CSS 单位

em 根据父元素的字体大小变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
<style>
body{
font-size: 14px;
}
div{
font-size: 1.2em;
}
</style>
<body>
<div class="test">
Test
</div>
</body>

rem 根据根元素的字体大小变化。

1
2
3
4
5
6
7
8
<style>
body{
font-size: 14px;
}
div{
font-size: 1.2rem;
}
</style>

vw 显示窗口的宽度
vh 显示窗口的高度
vmin 和 vmax 宽度和高度的最大值或最小值有关(如果宽度为1100px高度为700px 那么 1vmin 是 7px,1vmax 是 11px 反之亦然。)

1
2
3
4
5
6
<style>
body{
width: 100vw;
height: 100vh;
}
</style>

ex和ch单位 这两个单位只也依赖于font-family,因为它们被定为基于特殊字体的法案。


相关资料
CSS的一些单位,如rem、px、em、vw、vh、vm
七个你可能不了解的CSS单位

效果实现

web页面内容滑动效果

事件

  • touchstart事件:手指触摸时候触发(支持多指触发)
  • touchmove事件:手指在滑动的时候连续地触发。在这个事件发生期间,调用
  • preventDefault()事件可以阻止滚动,当然也不能滥用否则会影响原有页面的上下滚动等。
  • touchend事件:手指从屏幕上离开的时候触发
  • touchcancel事件:当系统停止跟踪触摸的时候触发。关于这个事件的确切出发时间,文档中并没有具体说明。。。不建议使用

相关资料
手把手教你实现web页面内容滑动效果

图像处理

函数

图片压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
const tool = {
//base64转file
dataURLtoFile (dataurl, filename) {
let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, {type: mime})
},

// 压缩图片
compress (img) {
let originWidth = img.width, // 压缩后的宽
originHeight = img.height,
maxWidth = 600, maxHeight = 600,
quality = 0.8, // 压缩质量
canvas = document.createElement('canvas'),
drawer = canvas.getContext('2d');
// 目标尺寸
let targetWidth = originWidth,
targetHeight = originHeight
// 图片尺寸超过300x300的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
canvas.width = targetWidth
canvas.height = targetHeight
drawer.clearRect(0, 0, canvas.width, canvas.height)
drawer.drawImage(img, 0, 0, canvas.width, canvas.height)
let base64 = canvas.toDataURL('image/jpeg', quality) // 压缩后的base64图片
let file = this.dataURLtoFile(base64, Date.parse(Date()) + '.jpg')
file = {content: base64, file: file}
console.log(file)//压缩后的file
return file
},
}
export default tool

图片压缩、方向纠正、预览、上传

实现原理

  • 用filereader读取用户上传的图片数据
  • 将img绘制到canvas上,用EXIF.js对图片方向进行纠正,再调用canvas.toDataURL对图片进行压缩,获取到压缩后的base64格式图片数据,转成二进制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1. 可以直接把base64数据转为File对象,如下:
// dataUrl数据转为File对象,dataUrl表示图片的base64数据
dataURLtoFile (dataurl, filename) {
var arr = dataurl.split(',')
var mime = arr[0].match(/:(.*?);/)[1]
var bstr = atob(arr[1])
var n = bstr.length; var u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], filename, { type: mime })
}
2. 或者你想把上文 方法返回的blob对象转为File对象,可以这样写:
/**
* Blob对象转为File对象
* @param {object} blob 二进制对象
* @param {string} fileName 文件名称
* @return {object} blob改造后的file文件对象。
* 注意:file对象就是一个特殊的blob对象
*/
function blobToFile(blob, fileName){
blob.lastModifiedDate =new Date();
blob.name = fileName;
return blob;
}

工具

  • RECOMPRESSOR
    在线图片优化工具,在文件尺寸和质量之间选择完美平衡。

  • 智图

在线高效优质图片优化,大图需要下载客户端。

  • Pica
    减小大图像的上传大小,节省上传时间。
    在图像处理上节省服务器资源。
    在浏览器中生成缩略图。

注意。如果您需要调整File/Blob的大小(通过表单的文件输入),请考虑使用 image-blob-reduce。

  • Lena.js
    用于图像处理的微型库,有 22 个图像滤镜。

  • Compressor.js
    JavaScript图像压缩器。使用浏览器的本机canvas.toBlob API进行压缩工作,这意味着它是有损压缩。通常使用此方法在上载客户端映像文件之前对其进行预压缩。

  • Fabric.js
    使用 canvas JavaScript 在网页上轻松创建简单的形状(如矩形,圆形,三角形和其他多边形)或由许多路径组成的更复杂的形状。Fabric.js将允许您使用鼠标来操纵这些对象的大小,位置和旋转。

  • blurify
    用于模糊图片

  • merge-images
    图像合并

  • cropperjs
    JavaScript 图像裁剪,旋转,缩放。

  • marvinj
    滤镜、裁剪

  • grade
    提供图像中的前2种主要颜色生成的互补渐变


相关资料
最优图像优化
智图
手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件
图片压缩原理
图片懒加载从简单到复杂

高质量前端快照方案:来自页面的「自拍」
一个有趣的例子带你入门canvas
了解 JS 压缩图片,这一篇就够了


HTMLCanvasElement.toDataURL
CanvasRenderingContext2D.drawImage()
FileReader
在web应用程序中使用文件
FormData 对象的使用
Using XMLHttpRequest

You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.