八種響應式VUE動效遮罩轉場動畫開發
阿新 • • 發佈:2019-02-05
簡介:使用前端技術,實現後臺管理介面的可供預覽視訊轉場特效,可以選擇資源後再選擇對應的特效元件進行轉場預覽,然後組合資料發向後端,在由後端推送到安卓端進行對應的視訊轉場切換。
- 使用技術:vue 、stylus、jquery、svg(核心)、 javascript
- 目前包括八種轉場: 時鐘、百葉窗、扇形開啟、十字擴充套件、分割、覆蓋、棋盤推進、溶解
- 核心技術棧:原生javascript、svg中的 viewBox、preserveAspectRatio、path、clip-path、defs、use、rect、image
- 每個動畫具體核心實現:
- 時鐘動畫:( 1.sector.vue )
- 原理:圓的座標公式、transform座標移動、path路徑
- 核心程式碼:
-
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width='100%' height='100%' viewBox='0 0 300 200' class="svgBox"> <!-- 定義外部遮罩 --> <clipPath id="clipPathDefinition" clipPathUnits="userSpaceOnUse" > <rect x='0' y='0' width='300' height='200'/> </clipPath> <!-- 定義扇形運動路徑 --> <defs> <path d="" stroke="#000" class="motionPath" transform="translate(150,100)" id="ring"></path> </defs> <!-- 定義遮罩 --> <clipPath id="1_SVGID_2_"> <use xlink:href="#ring" style="overflow:visible;"/> </clipPath> <!-- 背景圖片 --> <image xlink:href="http://img05.tooopen.com/images/20150521/tooopen_sy_125610923736.jpg" x="0" y="0" height="100%" width="100%" preserveAspectRatio="xMidYMid slice"/> <!-- 要繪製的扇形 --> <g clip-path="url(#1_SVGID_2_)"> <image xlink:href="" x="-150" y="-100" width="600" height="400" preserveAspectRatio="xMaxYMax meet" class="cp-img" /> </g> </svg>
- 預覽效果:
- 百葉窗動畫:( 2.windows.vue )
- 原理:多個rect遮罩形狀運動
- 核心程式碼:
<!-- 單個遮罩塊 --> <g> <defs> <rect id="2_SVGID_15_" y="120" width="300" height="40" class="svgRect" /> </defs> <clipPath id="2_SVGID_16_"> <use xlink:href="#2_SVGID_15_" style="overflow:visible;"/> </clipPath> <g style="clip-path:url(#2_SVGID_16_);"> <defs> <rect id="2_SVGID_17_" width="300" height="200"/> </defs> <clipPath id="2_SVGID_18_"> <use xlink:href="#2_SVGID_17_" style="overflow:visible;"/> </clipPath> <g transform="matrix(1 0 0 1 -3.814697e-06 0)" style="clip-path:url(#2_SVGID_18_);"> <image style="overflow:visible;" width="1920" height="1080" :xlink:href="cimg" transform="matrix(0.1958 0 0 0.2176 -53 -11)"> </image> </g> </g> </g>
methods:{ initEvent(){ var This = this; //2. 百葉窗 $('.sbox-item-2').click(function(){ console.log('motion 2'); This.motion($(this),$(this).find('.svgRect'),$(this).find('.cp-img')); }); }, motion(wrapper,elem,img){ var This = this,h = 40; img.attr('xlink:href',this.imgSrc); wrapper.addClass('sbox-item-active'); cancelAnimationFrame(this.timer); fn(); function fn(){ This.timer = requestAnimationFrame(fn); This.$emit('getTimer',This.timer); h-=0.3; //h = parseInt(h); elem.each(function(index,ele){ ele.setAttribute('height',h); }); if(h < 0.5){ h = 40; } } } }
- 預覽效果:
- 扇形開啟:( 3.sx.vue )
- 原理:兩個半圓形狀同時展開運動
- 核心程式碼:
<!-- 結構 --> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width='100%' height='100%' viewBox='0 0 300 200' class="svgBox"> <!-- 定義外部遮罩 --> <clipPath id="clipPathDefinition" clipPathUnits="userSpaceOnUse" > <rect x='0' y='0' width='300' height='200'/> </clipPath> <!-- 定義扇形運動路徑 --> <defs> <path d="" stroke="#000" class="motionPath—1" id="rings-1"></path> <path d="" stroke="#000" class="motionPath-2" id="rings-2"></path> </defs> <!-- 定義遮罩 --> <clipPath id="3_SVGID_2_"> <use xlink:href="#rings-1" style="overflow:visible;"/> <use xlink:href="#rings-2" style="overflow:visible;"/> </clipPath> <!-- 背景圖片 --> <image xlink:href="http://img05.tooopen.com/images/20150521/tooopen_sy_125610923736.jpg" x="0" y="0" height="100%" width="100%" preserveAspectRatio="xMidYMid slice"/> <!-- 要繪製的扇形 --> <g clip-path="url(#3_SVGID_2_)"> <image xlink:href="" x="-150" y="-100" width="600" height="400" preserveAspectRatio="xMaxYMax meet" class="cp-img" /> </g> </svg>
/* 行為 */ sectorfn(wrapper,elem1,elem2,img){ var This = this,r = 250,cx = 150,cy = 100,degrees = 0,rad = 0,srad = 0; img.attr('xlink:href',this.cimg); elem1.get(0).setAttribute('transform', 'translate('+150+','+100+')'); elem2.get(0).setAttribute('transform', 'translate('+150+','+100+')'); wrapper.addClass('sbox-item-active'); cancelAnimationFrame(This.timer); fn(); function fn(){ This.timer = requestAnimationFrame(fn); This.$emit('getTimer',This.timer); degrees+=1; rad = degrees * (Math.PI / 180); srad = (degrees) * (Math.PI / 180); if(degrees > 180){ degrees = 0; } var x1 = (Math.sin(rad) * r).toFixed(2); var y1 = -(Math.cos(rad) * r).toFixed(2); var x2 = -(Math.sin(srad) * r).toFixed(2); var y2 = -(Math.cos(srad) * r).toFixed(2); var lenghty1 = window.Number(degrees > 180); var lenghty2 = window.Number(degrees < 180); var descriptions1 = [ 'M', 0, -r, 'A', r, r, 0, lenghty1, 1, x1, y1, 'L',0,0,'Z']; var descriptions2 = [ 'M', 0, -r, 'A', r, r, 0, 0, 0, x2, y2, 'L',0,0,'Z']; elem1.attr('d', descriptions1.join(' ')); elem2.attr('d', descriptions2.join(' ')); } }
- 預覽效果:
- 十字擴充套件:( 4.shizi.vue )
- 原理:兩個rect遮罩width、height、x、y同時運動,保持位置居中
- 核心程式碼:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width='100%' height='100%' viewBox='0 0 300 200' class="svgBox"> <!-- 定義外部遮罩 --> <clipPath id="clipPathDefinition" clipPathUnits="userSpaceOnUse" > <rect x='0' y='0' width='300' height='200'/> </clipPath> <!-- 定義扇形運動路徑 --> <defs> <rect x='0' y='0' width='300' height='200' id="rects-1"/> <rect x='0' y='0' width='300' height='200' id="rects-2"/> </defs> <!-- 定義遮罩 --> <clipPath id="4_SVGID_2_"> <use xlink:href="#rects-1" style="overflow:visible;"/> <use xlink:href="#rects-2" style="overflow:visible;"/> </clipPath> <!-- 背景圖片 --> <image xlink:href="http://img05.tooopen.com/images/20150521/tooopen_sy_125610923736.jpg" x="0" y="0" height="100%" width="100%" preserveAspectRatio="xMidYMid slice"/> <!-- 要繪製的扇形 --> <g clip-path="url(#4_SVGID_2_)"> <image xlink:href="" x="0" y="0" width="300" height="200" preserveAspectRatio="xMaxYMax slice" class="cp-img" /> </g> </svg>
- 預覽效果:
- 分割動畫:( 5.fenge.vue )
- 原理:橫向rect遮罩width、height、x、y同時運動,保持位置居中 (其結構與上類似)
- 核心程式碼:
//分割動畫 sectorfn(wrapper,elem1,img){ var This = this,w = 300,x = 0,width = 300; img.attr('xlink:href',this.cimg); wrapper.addClass('sbox-item-active'); cancelAnimationFrame(This.timer); fn(); function fn(){ This.timer = requestAnimationFrame(fn); This.$emit('getTimer',This.timer); w-=1.5; x = (width-w)/2; if(w <= 0){ w = 300; x = 0; } if(w > 0){ elem1.attr({x:x,'width':w}); } } }
- 預覽效果:
- 覆蓋動畫:( 6.cover.vue )
- 棋盤推進:( 7.chess.vue )
- 原理:DOM動畫、動態排列位置,每個位置的盒子背景圖合成一個完整的背景圖(也可以用svg中的pattern來做 ,但生成的標籤太多,沒有backgroud-image來得快)
- 核心程式碼:
methods:{ initEvent(){ var This = this; $('.sbox-item-7').click(function(){ This.sectorfn($(this),$(this).find('.cb-box')); }); }, initDraw(){ var row = 8; var col = 5; var width = $('.chess-box').width(); var height = $('.chess-box').height(); var rw = Math.floor(width/6); var rh = Math.floor(height/col); var crw = Math.floor(width/row); var cstr = ''; var rstr = ''; var astr = ''; for(var i=0;i<col;i++){ var cstr = `<div class="cb-row-box" style="width:${rw*row}px;height:${rh}px;top:${rh*i}px;left:${ i%2==0 ? -parseInt(rw/2) : 0 }px;">`; rstr = ''; for(var j=0;j<row;j++){ rstr += `<div class="cb-box" style="width:${rw}px;height:${rh}px;left:${rw*j}px;top:0px; background-image:url('http://hiphotos.baidu.com/image/w=730;crop=0,0,730,405/sign=8630d99f963df8dca63d8d92fd2a11f9/0824ab18972bd407c305049572899e510eb3099c.jpg');background-size:${width*8/6}px ${height*8/6}px;background-position:${ i%2==0 ? -rw*j : -rw*j-parseInt(rw/2) }px ${-rh*i}px;" data-row="${i} "></div>`; } cstr = cstr + rstr + '</div>'; astr += cstr; } $('#chess-box').html(astr); }, //1. 互動 sectorfn(wrapper,elem){ var This = this,row = 8,col = 5,width = $('.chess-box').width(),rw = width/(row-2),w1 = 0,w2 = 0,count = 0; elem.each(function(index,ele){ $(ele).css({ 'width': 0, 'background-image': "url("+This.tiimg+")" }); }); wrapper.addClass('sbox-item-active'); cancelAnimationFrame(This.timer); fn(); function fn(){ This.timer = requestAnimationFrame(fn); This.$emit('getTimer',This.timer); count+=0.5; if(count > 20){ w2+=0.6; } w1+=0.6; elem.each(function(index,ele){ if(ele.dataset.row % 2 == 0){ $(ele).css({ 'width': w1 }); }else{ $(ele).css({ 'width': w2 }); } }); if(w1 > rw){ w1 = rw; } if(w2 > rw){ w2 = rw; cancelAnimationFrame(This.timer); return; } } } }
- 預覽效果:
- 溶解:( 8.diss.vue )
- 原理:同上棋盤推進、只是需要把每個box的位置讓在陣列中打亂後,逐個出現即可
- 核心程式碼:(其結構與行為和上面的棋盤推進效果類似)
methods:{ initEvent(){ var This = this; $('.sbox-item-8').click(function(){ This.sectorfn($(this),$(this).find('.cb-box')); }); }, initDraw(){ var row = 8; var col = 7; var width = $('.diss-box').width(); var height = $('.diss-box').height(); var rw = Math.floor(width/6); var rh = Math.floor(height/col); console.log(rh) var crw = Math.floor(width/row); var cstr = ''; var rstr = ''; var astr = ''; var cindex = 0; for(var i=0;i<col;i++){ var cstr = `<div class="cb-row-box" style="width:${rw*row}px;height:${rh}px;top:${rh*i}px;left:${ i%2==0 ? -parseInt(rw/2) : 0 }px;">`; rstr = ''; for(var j=0;j<row;j++){ cindex++; rstr += `<div class="cb-box" style="width:${rw}px;height:${rh}px;left:${rw*j}px; top:0px; opacity:0; background-image:url('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1532844894449&di=2a046505cf28d75564baa61a9adc7d52&imgtype=0&src=http%3A%2F%2Fold.bz55.com%2Fuploads%2Fallimg%2F150422%2F139-1504221GZ3.jpg');background-size:${width*8/6}px ${height*8/6}px;background-position:${ i%2==0 ? -rw*j : -rw*j-parseInt(rw/2) }px ${-rh*i}px;" data-row="${i} data-index="${cindex}" ></div>`; } cstr = cstr + rstr + '</div>'; astr += cstr; } $('#diss-box').html(astr); }, //1. 互動 sectorfn(wrapper,elem){ var This = this,row = 8,col = 7,width = $('.chess-box').width(),rw = width/(row-2),w1 = 0,w2 = 0,count = 0,ci = 0,len = row*col,indexArr = []; //get 當前圖片 elem.each(function(index,ele){ $(ele).css({ 'background-image': "url("+This.tiimg+")", 'opacity': 0, 'transition': 'ease .5s' }); }); //打亂index陣列陣列順序 for(var i=0;i<=len;i++){ indexArr.push(i); } indexArr.sort(function(a,b){ return Math.random()-0.5; }); //新增點選後樣式 wrapper.addClass('sbox-item-active'); cancelAnimationFrame(This.timer); fn(); function fn(){ This.timer = requestAnimationFrame(fn); This.$emit('getTimer',This.timer); count+=0.5; if(count % 2 == 0){ elem.eq(indexArr[ci]).css('opacity',1); elem.eq(indexArr[ci+1]).css('opacity',1); ci+=2; } if(ci > indexArr.length+3){ cancelAnimationFrame(This.timer); return; } } } }
- 預覽效果:
14. 彈性佈局、結合viewBox和圖片的preserveAspectRatio屬性來滿足各尺寸相容性,後續需要結合其他元件進行圖片資源、動畫狀態等通訊
15. 完成!