简介:使用前端技术,实现后台管理界面的可供预览视频转场特效,可以选择资源后再选择对应的特效组件进行转场预览,然后组合数据发向后端,在由后端推送到安卓端进行对应的视频转场切换。
- 使用技术: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. 完成!