2018-06-14: JS 自定義 截圖、裁剪

lynn_發表於2018-06-14

--依賴於jQuery;

1、呼叫方式:
view:

<img id="draftPhoto" src="{{photoSrc}}" alt="待截圖圖片" title="待截圖圖片"/>
複製程式碼

這裡的 photoSrc 可以是本地圖片地址、也可以是網路圖片地址(需要跨域)、還可以是Base64編碼圖片
jQuery:

$('#draftPhoto').photoCrop({
	img : $scope.tempdraftPhoto,
	fixedScale:false,
	isHead:false,
	callBack:function(data){
		if(data && data.success){
			//取出返回結果值 - 返回結果值根據情況做取捨
			var type = data.type, imgBase64Url = data.imgBase64Url;
			console.log(type);
			console.log(imgBase64Url);
		}else{
			tipsShow("截圖失敗, 回撥引數錯誤!");
		}
	}
});
複製程式碼

2、程式碼實現:

(function ($){
    $.fn.photoCrop=function (option) {
	//預設引數
	 var opt={
		 img:'', //圖片src
		 fixedScale:9/5, //縮放
		 isHead:null, //圓形
		 maxWidth:'860', //最大寬度
		 maxHeight:'1080', //最大高度
		 callBack:function () {} //回撥
	 };
	 opt=$.extend(opt,option);//覆蓋預設引數
	 var _this=this;//作用於
	 var imgSrc=opt.img ? opt.img : _this.attr('src');//圖片Src
	 
	 //裁剪主介面 - 包括 遮罩 和 圖片顯示 + 操作按鈕
	 var photoCropBox=$('<div id="photoCropBox" style="position: fixed;width: 100%;height: 100%;top: 0;left: 0;background: rgba(0,0,0,0.5);z-index: 1060;overflow:auto;overflow-y: scroll;">' +
			 '<canvas id="cropCanvas" style="position: absolute;opacity:1;left: 0;top: 0;"></canvas>' +
			 '<img id="dataImg" src="'+imgSrc+'" style="opacity: 0;position: absolute;" alt="">' +
			 '<div id="photoCropBox-panel-box" style="position: relative;width: 100%;height: 100%;">' +
			 '<div id="photoCropBox-panel" style="opacity:0;background: #eee;border-radius: 5px;max-width: '+opt.maxWidth+'px;position: absolute; top: 40px; text-align: center;">' +
			 '<div id="photoCropBox-option1" style="text-align: left;padding-left: 30px;padding-top: 20px;position: relative;z-index: 2">' +
			 '<span id="frontageImg" class="photoCropBox-start">正面截圖</span><span class="photoCropBox-end">確 定</span><span class="photoCropBox-cancel">取 消</span>' +
			 '</div> ' +
			 '<div id="photoCropBox-img" style="margin: 30px;display: inline-block;position: relative">' +
			 '<img src="'+imgSrc+'" style="display: block;max-width: 100%;max-height: '+(opt.maxHeight-100)+'px;"></div>' +
			 '<div id="photoCropBox-option2" style="text-align: right;padding-right: 50px;padding-bottom: 20px;position: relative;z-index: 2">' +
			 '<span id="backImg" class="photoCropBox-start">背面截圖</span><span class="photoCropBox-end">確 定</span><span class="photoCropBox-cancel">取 消</span>' +
			 '</div>' +
			 '</div>' +
			 '</div></div>');
	 $('body').append(photoCropBox);
	 //原圖顯示容器
	 var _box=$('#photoCropBox-img');
	 var imgWidth=_box.find('img').width();
	 //操作按鈕
	 $('#photoCropBox-option1 span, #photoCropBox-option2 span').css({
		 lineHeight:'26px', background:'#888', color:'#eee', display:'inline-block',
		 paddingLeft:'10px', paddingRight:'10px', marginRight:'8px', cursor:'pointer'
	 });
	 //裁剪 - 截圖區域 - 邊框線 + 原圖背景 + 縮放點
	 var cropBox=$('<div id="photoCropBox-cropBox" style="position: absolute;z-index: 5;cursor: Move;display: none;">' +
	 '<div id="cropBoxLine" style="overflow: hidden;position: absolute;width: 100%;height: 100%;">' +
	 '<img src="'+imgSrc+'" style="display: block;width: '+imgWidth+'px;position: absolute;max-height: none;max-width: none;">' +
	 '<div class="top line" style="width: 100%;height: 1px;top: 0;left: 0;"></div><div class="right line" style="height: 100%;width: 1px;top: 0;right: 0;"></div>' +
	 '<div class="line bottom" style="width: 100%;height: 1px;bottom: 0px;left: 0"></div><div class="left line" style="height: 100%;width: 1px;top: 0;left: 0;"></div></div>' +
	 '<div id="cropBoxLine2"><div class="left line2" style="height: 100%;width: 1px;top: 0;left: 0;cursor: w-resize;"></div>' +
	 '<div class="right line2" style="height: 100%;width: 1px;top: 0;right: 0;cursor: e-resize;"></div>' +
	 '<div class="top line2" style="width: 100%;height: 1px;top: 0;left: 0;cursor: n-resize;position: absolute"></div>' +
	 '<div class="bottom line2" style="width: 100%;height: 1px;bottom: 0px;left: 0;cursor: s-resize;"></div>' +
	 '<div class="left bot" style="left: 0;top: 50%;margin-top: -4px;cursor: w-resize;"></div>' +
	 '<div class="right bot" style="right: 0;top: 50%;margin-top: -4px;cursor: e-resize;"></div>' +
	 '<div class="bottom bot" style="bottom: 0;left: 50%;margin-left: -4px;cursor: s-resize;"></div>' +
	 '<div class="top bot" style="top: 0;left: 50%;margin-left: -4px;cursor: n-resize;"></div>' +
	 '<div class="left-top bot" style="left: 0;top: 0;cursor: nw-resize;"></div>' +
	 '<div class="left-bottom bot" style="left: 0;bottom: 0;cursor: sw-resize;"></div>' +
	 '<div class="right-top bot" style="right: 0;top: 0;cursor: ne-resize;"></div>' +
	 '<div class="right-bottom bot"style="right: 0;bottom: 0;cursor: se-resize;"></div></div></div>');
	 //截圖區域背景
	 var screen=$('<div id="photoCropBox-bg" style="background: rgba(0,0,0,.5);position: absolute;left: 0;top: 0;width: 100%;height: 100%;z-index: 4;cursor: crosshair;display: none;"></div>');
	 _box.append(cropBox).append(screen);
	 //裁剪 - 截圖區域DOM
	 var _corp=$('#photoCropBox-cropBox');
	 //截圖區域 選擇線
	 var cropBoxLine=$('#cropBoxLine');
	 setTimeout(function () {
		 cropBoxLine.find('img').css('width',_box.find('img').width()+'px');
	 },20);
	//圓形
	 if(opt.isHead){
		 cropBoxLine.css({borderRadius:'100%'});
	 }
	 $('#photoCropBox-cropBox .line,#photoCropBox-cropBox .line2').css({
		 position:'absolute', opacity:.5
	 });
	 $('#photoCropBox-cropBox .bot').css({
		 position:'absolute', width:8, height:8,
		 background:'#0f0', border:'1px #999 solid'
	 });
	 setTimeout(function () {
		 init();
	 },10);
	 $(window).on('resize',function () {
		 setPosition();
	 });
	//取消
	 $('.photoCropBox-cancel').on('click',function () {
		 closeBox();
	 });
	//按下 裁剪選擇區域
	 $('#photoCropBox-bg').on('mousedown',function (e) {
		 if(opt.fixedScale) {return;} //固定
		 $('#cropBoxLine2').hide();
		 var _this=$(this), 
		 	_sx=e.pageX,
		 	_sy=e.pageY, 
		 	_tx=_this.offset().left, 
		 	_ty=_this.offset().top;
		 $(document).on('mousemove',function (e) {
			 e.preventDefault();
			 var _ex=e.pageX,_ey=e.pageY;
			 getPosition(_ex,_ey,_ty,_tx,_sx,_sy,_this);
		 });
		 $(document).on('mouseup',function () {
			 $(document).unbind('mousemove');
			 $('#cropBoxLine2').show();
		 });
	 });
	 var lock=false;
	 //按下裁剪 - 截圖區域
	 _corp.on('mousedown',function (e) {
		 if(lock){return;}
		 var _this=$(this),
		 	_sx=e.pageX, 
		 	_sy=e.pageY, 
		 	_thisX=parseInt(_this.css('left')),
		 	_thisY=parseInt(_this.css('top')),
		 	_thisW=parseInt(_this.css('width')),
		 	_thisH=parseInt(_this.css('height')),
		 	pW=$('#photoCropBox-bg').width(),
		 	pH=$('#photoCropBox-bg').height();
		 $(document).on('mousemove',function (e) {
			 e.preventDefault();
			 var _ex=e.pageX, _ey=e.pageY, _x=Number(_ex)-Number(_sx), _y=Number(_ey)-Number(_sy);
			 _x+=_thisX;
			 _y+=_thisY;
			 if(_x<0) {_x=0;}
			 if(_y<0) {_y=0;}
			 if(_y>pH-_thisH) {_y=Number(pH)-Number(_thisH);}
			 if(_x>pW-_thisW) {_x=Number(pW)-Number(_thisW);}
			 resizeCropBox("","",_y,_x,true)
		 });
		 $(document).on('mouseup',function () {
			 $(document).unbind('mousemove');
		 });
	 });
	//控制大小
	 $('#cropBoxLine2 .bot').on("mousedown",function (e) {
		 lock=true;
		 var _esx=e.pageX, _esy=e.pageY, 
		 	_that=$(this), 
		 	_this=$('#photoCropBox-bg'), 
		 	_tx=_this.offset().left, 
		 	_ty=_this.offset().top, 
		 	_sx=_corp.offset().left,
		 	_sy=_corp.offset().top;//裁剪框
		 if(_that.hasClass('right-top')) {_sy+=_corp.height();}
		 if(_that.hasClass('left-top')){
			 _sy+=_corp.height();
			 _sx+=_corp.width();
		 }
		 if(_that.hasClass('left-bottom')) {_sx+=_corp.width();}
		 $(document).on('mousemove',function (e) {
			 e.preventDefault();
			 var _ex=e.pageX,_ey=e.pageY;
			 if(opt.fixedScale){
				 _ey=Number((Number(_ex)-Number(_esx))/opt.fixedScale)+Number(_esy);
				 if(_that.hasClass('right-top') || _that.hasClass('left-bottom')){
					 _ey=Number((Number(_esx)-Number(_ex))/opt.fixedScale)+Number(_esy);
				 }
			 }
			 getPosition(_ex,_ey,_ty,_tx,_sx,_sy,_this);
		 });
		 $(document).on('mouseup',function () {
			 $(document).unbind('mousemove');
			 lock=false;
		 })
	 });
	 $('#cropBoxLine2 .left,#cropBoxLine2 .top,#cropBoxLine2 .right,#cropBoxLine2 .bottom').on('mousedown',function (e) {
		 if(opt.fixedScale) {return;} //固定
		 lock=true;
		 var _that=$(this),
		 	_this=$('#photoCropBox-bg'),
		 	_tx=_this.offset().left,
		 	_ty=_this.offset().top,
		 	_sx=_corp.offset().left,
		 	_sy=_corp.offset().top,
		 	ch=_corp.height(),
		 	cw=_corp.width();
		 if(_that.hasClass('top')){
			 _sy+=ch;
		 }else if(_that.hasClass('left')) {
			 _sx+=cw;
		 }
		 $(document).on('mousemove',function (e) {
			 e.preventDefault();
			 var _ex=e.pageX,_ey=e.pageY;
			 if(_that.hasClass('top') || _that.hasClass('bottom')){
				 if(!(Number(_ey)-Number(_sy)>0)){
					 var _x=Number(_sx)-Number(_tx),_y=Number(_ey)-Number(_ty),_w=cw,_h=-(Number(_ey)-Number(_sy));
					 if(_y<0) {_y=0;_h=Number(_sy)-Number(_ty);}
				 }else{
					 var _x=Number(_sx)-Number(_tx),_y=Number(_sy)-Number(_ty),_w=cw,_h=Number(_ey)-Number(_sy);
					 if(_h>Number(_this.height())-Number(_y)) {_h=Number(_this.height())-Number(_y);}
				 }
			 }else {
				 if(Number(_ex)-Number(_sx)>0 && Number(_ey)-Number(_sy)>0){
					 var _x=Number(_sx)-Number(_tx),_y=Number(_sy)-Number(_ty),_w=Number(_ex)-Number(_sx),_h=ch;
					 if(Number(_w)>Number(_this.width())-Number(_x)) _w=Number(_this.width())-Number(_x);
				 }else if(!(Number(_ex)-Number(_sx)>0) && Number(_ey)-Number(_sy)>0){
					 var _x=Number(_ex)-Number(_tx),_y=Number(_sy)-Number(_ty),_w=-(Number(_ex)-Number(_sx)),_h=ch;
					 if(_x<0) {_x=0;_w=Number(_sx)-Number(_tx);}
				 }
			 }
			 resizeCropBox(_w,_h,_y,_x);
		 });
		 $(document).on('mouseup',function () {
			 $(document).unbind('mousemove');
			 lock=false;
		 });
	 });
	 var sectionType = "";
	 //開始裁剪
	 $('.photoCropBox-start').on('click',function () {
		 sectionType = this.id;
		 _corp.css('display','block');
		 $('#photoCropBox-bg').css('display','block');
		 init();
	 });
	 //確認 裁剪區域
	 $('.photoCropBox-end').on('click',function () {
		 if(sectionType){
			 getImage();
		 }
		 closeBox();
	 });
	//初始化
	 function init() {
		 setPosition();
		 if(opt.fixedScale){
			 if((_box.height()-_box.width()/opt.fixedScale/2)<0){
				 resizeCropBox(_box.height()*opt.fixedScale,_box.height(),0,(_box.width()-_box.height()*opt.fixedScale)/2);
			 }else {
				 resizeCropBox(_box.width()/2,_box.width()/opt.fixedScale/2,(_box.height()-_box.width()/opt.fixedScale/2)/2,_box.width()/4);
			 }
		 }else {
			 //預設選擇區域
			 if(sectionType == 'frontageImg'){
				 resizeCropBox(_box.width()-200,_box.height()/2-100,50,100);
			 }else if(sectionType == 'backImg'){
				 resizeCropBox(_box.width()-200,_box.height()/2-100,_box.height()/2+50,100);
			 }else{
				 resizeCropBox(_box.width()-200,_box.height()-100,50,100);
			 }
		 }
		 if(opt.fixedScale) {
			 $('.bot.top,.bot.left,.bot.bottom,.bot.right').remove();//固定
		 }
	 };
	 //設定彈出層皮膚位置
	 function setPosition() {
		 $('#photoCropBox-panel').css({
			 left:Math.abs($('#photoCropBox-panel-box').width()-$('#photoCropBox-panel').width())/2+'px',
			 opacity:1
		 });
	 };
	//結束x,y 背景x,y
	 function getPosition(_ex,_ey,_ty,_tx,_sx,_sy,_this) {
		 if(_ex-_sx>0 && _ey-_sy>0){
			 var _x=_sx-_tx,_y=_sy-_ty,_w=_ex-_sx,_h=_ey-_sy;
			 if(_w>_this.width()-_x) {_w=_this.width()-_x;}
			 if(_h>_this.height()-_y) {_h=_this.height()-_y;}
		 }else if(!(_ex-_sx>0) && _ey-_sy>0){
			 var _x=_ex-_tx,_y=_sy-_ty,_w=-(_ex-_sx),_h=_ey-_sy;
			 if(_x<0) {_x=0;_w=_sx-_tx;}
			 if(_h>_this.height()-_y) {_h=_this.height()-_y;}
		 }else if(!(_ex-_sx>0) && !(_ey-_sy>0)){
			 var _x=_ex-_tx,_y=_ey-_ty,_w=-(_ex-_sx),_h=-(_ey-_sy);
			 if(_x<0) {_x=0;_w=_sx-_tx;}
			 if(_y<0) {_y=0;_h=_sy-_ty;}
		 }else if(_ex-_sx>0 && !(_ey-_sy>0)){
			 var _x=_sx-_tx,_y=_ey-_ty,_w=_ex-_sx,_h=-(_ey-_sy);
			 if(_y<0) {_y=0;_h=_sy-_ty;}
			 if(_w>_this.width()-_x) {_w=_this.width()-_x;}
		 }
		 if(opt.fixedScale){
			 if(_w/opt.fixedScale>_h){
				 _w=_h*opt.fixedScale;
			 }else if (_w<opt.fixedScale*_h){
				 _h=_w/opt.fixedScale;
			 }
		 }
		 resizeCropBox(_w,_h,_y,_x);
	 };
	//畫布
	 var c=document.getElementById("cropCanvas"), 
	 	ctx=c.getContext("2d"), 
	 	callBackReturnData = {success : false};
	 function getImage() {
		 /**
		  * 注意這裡的 #dataImg 需要獲取原始寬高, 用於計算與截圖圖片的寬高比
		  * HTML5提供了一個新屬性naturalWidth/naturalHeight可以直接獲取圖片的原始寬高。這兩個屬性在Firefox/Chrome/Safari/Opera及IE9裡已經實現
		  */
		 var scaleW=cropBoxLine.find('img').width()/document.getElementById('dataImg').naturalWidth,
		 	scaleH=cropBoxLine.find('img').height()/document.getElementById('dataImg').naturalHeight,
		 	//開始剪下的 x 座標位置
		 	sx=parseInt(_corp.css('left'))/scaleW,
		 	//開始剪下的 y 座標位置
		 	sy=parseInt(_corp.css('top'))/scaleH,
		 	//被剪下影像的寬度
		 	swidth=parseInt(_corp.css('width'))/scaleW,
		 	//被剪下影像的高度
		 	sheight=parseInt(_corp.css('height'))/scaleH,
		 	//要使用的影像、畫布
		 	c_img = new Image();
		 c_img.crossOrigin = 'Anonymous'; //可選值:anonymous,*
		 c_img.src = imgSrc;
		 callBackReturnData.type = sectionType;
		 //畫布寬高 - 縮小2倍
		 c.width = swidth/2;
		 c.height = sheight/2;
		 c_img.onload = function () {
			 /**
			  * 1、c_img : 規定要使用的影像、畫布或視訊
			  * 2、sx : 可選。開始剪下的 x 座標位置
			  * 3、sy : 可選。開始剪下的 y 座標位置
			  * 4、swidth : 可選。被剪下影像的寬度
			  * 5、sheight : 可選。被剪下影像的高度
			  * 6、0 : 在畫布上放置影像的 x 座標位置
			  * 7、0 : 在畫布上放置影像的 y 座標位置
			  * 8、swidth/2 : 可選。要使用的影像的寬度(伸展或縮小影像)
			  * 9、sheight/2 : 可選。要使用的影像的高度(伸展或縮小影像)
			  */
			 ctx.drawImage(c_img,sx,sy,swidth,sheight,0,0,swidth/2,sheight/2);
			 //獲取圖片BASE64
			 var url=c.toDataURL("image/jpeg");
			 
			 //回撥返回值
			 callBackReturnData.imgBase64Url = url;
			 callBackReturnData.success = true;
			 opt.callBack(callBackReturnData);
		 };
		 c_img.onerror=function(){
			 alert("稿樣圖片請求失敗:CORS policy:  'Access-Control-Allow-Origin' header");
		 }
	 };
	 //寬,高,top,left,m-是否是拖拽
	 function resizeCropBox(w,h,t,l,m) {
		 _corp.css(prefix()+'transition','all 0s');
		 if(!m){
			 _corp.css({
				 width:w+'px', height:h+'px',
				 top:t+'px', left:l+'px'
			 });
		 }else {
			 _corp.css({
				 top:t+'px', left:l+'px'
			 });
		 }
		 cropBoxLine.find('img').css({
			 top:-t+'px', left:-l+'px'
		 });
	 };
	 function closeBox() {
		 $('#photoCropBox').remove();
	 };
	 function prefix() {
		 var prefixes=['','-ms-','-moz-','-webkit-','-o-'],i=0;
		 while (i < prefixes.length){
			 if($('body').css(prefixes[i]+'transition')){
				 return prefixes[i];
			 }
			 i++;
		 }
	 };
}
})(jQuery);複製程式碼

相關文章