Skip to content

Commit

Permalink
feat: 添加内容
Browse files Browse the repository at this point in the history
  • Loading branch information
梁怀刚 committed Jul 12, 2024
1 parent df0a650 commit 2ab49b2
Show file tree
Hide file tree
Showing 14 changed files with 1,595 additions and 6 deletions.
131 changes: 131 additions & 0 deletions blog/2016-04-30-Web端裁剪图片方法.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
## Web端裁剪图片方法




由于在Web端,JavaScript不能直接处理本地文件,因此可以在后台裁剪图片,或者利用html5的canvas来处理。

### 方法1:传送到后台剪切

**步骤1:上传图片到后台,向前端返回图片URL**

利用input标签,将文件发送到后台。

可以使用jQuery中的ajaxFileUpload方法
```javascript
$.ajaxFileUpload( {
url: 'live/apply/uploadImage', //用于文件上传的服务器端请求地址
type:'post', secureuri: false, //一般设置为false
fileElementId: image, //文件上传空间的id属性
dataType: 'json', //返回值类型 一般设置为json
data:data, //可以传递图片属性及其他数据
success: function (data, status) //服务器成功响应处理函数 { //上传传成功处理 },
error: function (data, status, e)//服务器响应失败处理函数 { //上传失败处理 }
```
**步骤2: 进行裁剪,获取图片的坐标及长宽等值,传回后台**
这里一般是利用一个移动的div来获取剪截的动画效果,目前有多种jquery插件可以使用,本文使用的是Jcrop插件,比较简单方便。
```javascript
$("#myPhoto").Jcrop({
onChange: showPreview,
onSelect: showPreview,
aspectRatio: 1,
});
function showPreview(coords) {
if (parseInt(coords.w)) {
//计算预览区域图片缩放的比例,通过计算显示区域的宽度(与高度)与剪裁的宽度(与高度)之比得到
var rx = $("#preview_box").width() / coords.w;
var ry = $("#preview_box").height() / coords.h;
//通过比例值控制图片的样式与显示
$("#crop_preview").css({
width: Math.round(rx * $("#myPhoto").width()) + "px",
//预览图片宽度为计算比例值与原图片宽度的乘积
height: Math.round(rx * $("#myPhoto").height()) + "px",
//预览图片高度为计算比例值与原图片高度的乘积
marginLeft: "-" + Math.round(rx * coords.x) + "px",
marginTop: "-" + Math.round(ry * coords.y) + "px",
});
$("#X1").val(coords.x);
$("#Y1").val(coords.y);
$("#X2").val(coords.x2);
$("#Y2").val(coords.y2);
$("#W").val(coords.w);
$("#H").val(coords.h);
}
}
```
根据上述过程,可以将获取到的剪截横纵坐标和长宽数据发送到后台。
**步骤3:后台裁剪图片**
### 方法2:Html5的canvas技术
这个需要浏览器支持以下几个点,并且兼容性还没有进行测试:
File API\
Blob\
canvas
**步骤1:读取文件**
如方法1一样,需要用input标签来获取file,但是JavaScript不能直接操作文件,因此需要File API来处理。
```javascript
$("input[type=file]").change(function () {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function () {
// 通过 reader.result 来访问生成的 DataURL
var url = reader.result;
setImageURL(url);
};
reader.readAsDataURL(file);
});
var image = new Image();
function setImageURL(url) {
image.src = url;
}

```
**步骤2:获取裁剪坐标**
参照方法1中的步骤2
**步骤3:利用canvas重绘图片**
首先要设置剪截后的图片大小相等的canvas。
```javascript
// 以下四个参数由步骤2获得 var x, y, width, height;
var canvas = $(" ")[0],
ctx = canvas.getContext("2d");
ctx.drawImage(image, x, y, width, height, 0, 0, width, height); //重绘
$(document.body).append(canvas); //添加到文档中可以查看效果
```
**步骤4:保存图片**
我们要获取 canvas 中图片的信息,需要用 toDataURL 转换成上面用到的 DataURL 。 然后取出其中 base64 信息,再用window\.atob 转换成由二进制字符串。但 window\.atob 转换后的结果仍然是字符串,直接给 Blob 还是会出错。所以又要用Uint8Array 转换一下。
```javascript
var data=canvas.toDataURL();
// dataURL 的格式为 “data:image/png;base64,\*\*\*\*”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了
data=data.split(',')[1];
data=window.atob(data);
var ia = new Uint8Array(data.length); for (var i = 0; i < data.length; i++) { ia[i] = data.charCodeAt(i); }; // canvas.toDataURL 返回的默认格式就是 image/png
var blob=new Blob(\[ia], {type:"image/png"});
```
**步骤5:将blob数据发送至后台**
在后台可以将Blob格式的数据转换成image保存。
> 原文标题:Web端裁剪图片方法\
> 原文链接:[https://segmentfault.com/a/1190000004268074](https://segmentfault.com/a/1190000004268074?ref=zhelin.me)
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
## jQuery实现弹幕效果、文字水平滚动/垂直滚动插件


<!--kg-card-begin: markdown-->

在写这个插件之前,已经在很多专题页面需要用到弹幕,或者是文字滚动等效果,原理大致相同(也有细微区别),但一直没有封装成插件。也是在这些工作中尝试使用了多种方法,在这里也将一一列举其中的优缺点。

### **一、使用CSS Animation的解决方案**

使用CSS Animation这个解决方案并不好,但这也是我最开始的一个思路。我个人认为这是一个最差的解决方案,在实际使用过程中发现了许多意外的问题。它的兼容性倒是其次,更大的问题是弹幕元素的不可控制,以及窗口失去焦点后,动画暂停,但是JS并没有暂停。这就导致了重新恢复窗口焦点后,弹幕一堆的出现。是一个非常不好的效果。

**思路:**

使用@keyframes定义一个移动的动画,通过JS动态的加载弹幕元素到显示区域边缘,并指定相应的类。那么添加的元素就会执行我们定义的这个移动动画。那么这个弹幕的效果就实现了。当然这些元素都是绝对定位元素。

这个解决方案的思路大致就是这样,但其本身的局限性导致我不去使用它,就不拆开细说。

### 二、使用相对定位偏移的解决方案

使用这种方法较上一个方法效果好了很多,这个方法我之前都是用于列表滚动。并没有考虑用在弹幕这种效果上,那么该如何去实现呢?

**思路:**

这里的所说的相对定位并不是说设置元素的position值为relative。当然这样设置之后使用left/right/top/bottom来做偏移也是能实现这个效果的,但是在这里我是使用的margin来做偏移。效果与设置position值为relative一样。

**效果:**

因为当时无法提供json数据,所以数据都是渲染到页面上的。

**JS:**

```
var barrage = function (val) {
this.box = $("#j-head-list .ui-repeater");
this.item = $("#j-head-list .ui-repeater .ui-repeater-item");
//保存第一个列表盒子
this.listBox1 = $("#j-head-list .list_01");
this.listBox2 = $("#j-head-list .list_02");
// 保存数据
this.data = [];
// 需要暂停的元素
this.stopItem = null;
// 首次渲染长度
this.firstLen = 10;
this.init();
};
barrage.prototype = {
init: function () {
this.getData();
//this.news();
},
//获取并整理数据
getData: function () {
var self = this;
var item = $.makeArray(this.item);
for (var i in item) {
this.data.push(item[i].innerHTML.trim());
}
this.data.forEach(function (item, index, array) {
self.data[index] = item.replace(/\[tgc/g, "[");
});
this.box.html("");
this.renderer();
},
//新闻列表整理
news: function () {
var item = $(".news_list .ui-repeater").html().toString();
item = item.replace(/\[tgc/g, "[");
$(".news_list .ui-repeater").html(item);
},
//悬浮暂停
hoverStop: function () {
var self = this;
this.box.hover(function () {
var $this = $(this);
if ($this.hasClass("list_01")) {
self.stopItem = 1;
} else {
self.stopItem = 2;
}
}, function () {
self.stopItem = null;
})
}, //渲染数据
renderer: function () {
var self = this, start, dataLen = this.data.length;
//第一次渲染数据
var index = 0;
for (start = 0; start < this.firstLen; start++) {
var _html = '<li class="ui-repeater-item" style="display: inline-block">' + this.data[index] + '</li>';
if (start % 2 == 0) {
this.box.eq(0).append(_html);
} else {
this.box.eq(1).append(_html);
}
index++;
if (dataLen < this.firstLen) {
if (index == dataLen) {
index = 0
}
}
}
this.hoverStop();
//添加数据
var left1 = 0, left2 = 0, width1, width2;
dataMonitor();
getWidth();
var time = setInterval(function () {
//保存第一次宽度
if (left1 < width1 && self.stopItem != 1) {
self.listBox1.css("margin-left", -(++left1) + 'px')
} else if (left1 >= width1 && self.stopItem != 1) {
var _html = '<li class="ui-repeater-item" style="display: inline-block">' + self.data[++start] + '</li>';
self.listBox1.children().eq(0).remove();
self.listBox1.css("margin-left", '-1px');
self.listBox1.append(_html);
left1 = 1;
dataMonitor();
getWidth();
}
if (left2 < width2 && self.stopItem != 2) {
self.listBox2.css("margin-left", -(++left2) + 'px')
} else if (left2 >= width2 && self.stopItem != 2) {
var _html = '<li class="ui-repeater-item" style="display: inline-block">' + self.data[++start] + '</li>';
self.listBox2.children().eq(0).remove();
self.listBox2.css("margin-left", '-1px');
self.listBox2.append(_html);
left2 = 1;
dataMonitor();
getWidth();
}
}, 30);
function getWidth() {
width1 = self.box.eq(0).children().eq(0).css("width");
width2 = self.box.eq(1).children().eq(0).css("width");
width1 = parseInt(width1.substring(0, width1.indexOf('px'))) + 50;
width2 = parseInt(width2.substring(0, width2.indexOf('px'))) + 50;
}
function dataMonitor() {
if (start + 1 >= dataLen) {
start = -1;
}
}
}
};
new barrage({});
```

从上面的代码可以看出,其实这个方法也是有瑕疵的,速度调节我们需要依赖setInterval的时间,或者是每执行一次时间循环元素的偏移量增大一点。但是无论怎么写,都会有滚动的顿挫感,不够平滑。那有什么方法来解决这些问题呢?

### 三、基于requestAnimationFrame的解决方案

使用requestAnimationFrame应该是最好的一个解决方案了(就我的眼界来看),并结合translate3d() 做偏移,简直是完美。那么要使用requestAnimationFrame,我们就要知道什么事requestAnimationFrame,那么久简单的介绍一下:

**requestAnimationFrame:**

window\.requestAnimationFrame()这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数,以满足开发者操作动画的需求。这个方法接受一个函数为参,该函数会在重绘前调用。

如果你想做逐帧动画的时候,你应该用这个方法。这就要求你的动画函数执行会先于浏览器重绘动作。通常来说,被调用的频率是每秒60次,但是一般会遵循W3C标准规定的频率。如果是后台标签页面,重绘频率则会大大降低。

回调函数只会被传入一个DOMHighResTimeStamp参数,这个参数指示当前被 requestAnimationFrame 序列化的函数队列被触发的时间。因为很多个函数在这一帧被执行,所以每个函数都将被传入一个相同的时间戳,尽管经过了之前很多的计算工作。这个数值是一个小数,单位毫秒,精确度在 10 µs。

需要注意的是:如果想得到连贯的逐帧动画,函数中必须重新调用 requestAnimationFrame()。

**语法:**

```
*requestID* = window.requestAnimationFrame(*callback*); // Firefox 23 / IE10 / Chrome / Safari 7 (incl. iOS)
requestID = window.mozRequestAnimationFrame(*`callback`*); // Firefox < 23
requestID = window.webkitRequestAnimationFrame(callback); // Older versions Chrome/Webkit
```

我们可以看到上面有兼容性写法,是的,其本身兼容性不太好,但是支持大部分的现代浏览器。但这不符合我们工作的需要,这我们不用担心,Opera浏览器的技术师Erik Möller 把这个函数进行了封装,使得它能更好的兼容各种浏览器。

```
(function () {
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame) window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
}());
```

这样,我们就可以放心的使用这个方法了。但仅限于现代浏览器。IE9+是没问题的。至于它的其他信息可以点击[这里查询](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame?ref=zhelin.me)

**效果:**


需要源码的同学可以点击[这里下载](https://github.com/ZhelinCheng/jquery.Barrage?ref=zhelin.me)
Loading

0 comments on commit 2ab49b2

Please sign in to comment.