vue实现多轨道时间线拖拽显示效果代码
代码语言:html
所属分类:拖放
代码描述:vue实现多轨道时间线拖拽显示效果代码,类似于视频剪辑多个轨道裁剪拼筹,每个轨道都可以自定义,还可以新增轨道。
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <link type="text/css" rel="stylesheet" href="//repo.bfw.wiki/bfwrepo/css/normalize.5.0.css"> <style> .column { float: left; display: inline-block; padding: 3px 6px 3px 0; } body { font-family: 'Source Code Pro', monospace; font-size: 11px; color: #c9c9c9; background-color: #3d3d3d; overflow: hidden; -webkit-user-select: none; } button { border: none; font-family: 'Source Code Pro', monospace; text-transform: uppercase; background-color: #393939; color: #ddd; margin: 1px; } button.submit { background-color: #546E7A; } button.delete { background-color: #8c3636; } button:focus { outline: 0; } button:hover { background-color: rgba(20, 20, 20, 0.2); } button:active { background-color: #5d5d5d; } #content { width: 100%; overflow-y: scroll; position: absolute; top: 0; bottom: 0; } .dragscroll { cursor: -webkit-grab; } #switch-header { height: 35px; border-right: solid 1px #707070; top: 0; left: 0; position: absolute; background-color: #545454; z-index: 10; } #switch-header .av-features { float: left; display: inline-block; padding: 3px 6px 3px 0; padding-top: 18px; margin-left: 7px; } #switch-header .icon-ref { vertical-align: bottom; float: left; display: inline-block; padding: 3px 6px 3px 0; width: 13px; height: 13px; } #switches { height: auto; margin-top: 35px; border-right: solid 1px #707070; left: 0%; position: absolute; z-index: 2; box-shadow: -2px 0px 6px black; } #switches .layer-info { height: 20px; border-bottom: solid 1px rgba(0, 0, 0, 0.1); margin: 0; margin-top: -2px; padding-bottom: 2px; font-size: 10px; padding-left: 6px; overflow: hidden; line-height: 20px; } #switches .layer-info.group-selected { background-color: #46575c; } #switches .layer-info button { font-size: 11px; width: 13px; height: 13px; margin: 0; padding: 0; vertical-align: text-bottom; border-radius: 2px; } #switches .layer-info .av-features { float: left; display: inline-block; padding: 3px 6px 3px 0; } #switches .layer-info .index { float: left; display: inline-block; padding: 3px 6px 3px 0; width: 16px; text-align: right; } #switches .layer-info .label-color { float: left; display: inline-block; padding: 3px 6px 3px 0; } #switches .layer-info .layer-name { float: left; display: inline-block; padding: 3px 6px 3px 0; padding-left: 4px; padding: 2px 0; overflow: hidden; } .label-popup { position: absolute; background-color: #494949; width: 10%; z-index: 50; } .label-popup .label-picker { float: left; display: inline-block; padding: 3px 6px 3px 0; height: 18px; } .label-popup button { font-size: 11px; width: 13px; height: 13px; margin: 0; padding: 0; vertical-align: text-bottom; border-radius: 2px; } #timeline-scroll { padding-left: 4px; overflow-x: scroll; height: auto; top: 0; position: absolute; display: inline-block; } #timeline-header { height: 35px; background-color: #444; top: 0%; position: absolute; z-index: 4; width: 100%; } #timeline { height: 100%; } #playhead-handle { width: 20px; height: 20px; opacity: 0.5; margin-left: -10px; margin-top: -35px; position: relative; cursor: col-resize; z-index: 101; } #playhead-icon { width: 20px; height: 20px; border-left: solid 1px #4FC3F7; margin-top: -20px; position: relative; z-index: 100; } #playhead-icon svg { -webkit-transform: translate(-5.5px, -31px); } #playhead { border-left: solid 1px #4FC3F7; background-color: rgba(255, 255, 255, 0.1); box-sizing: border-box; height: 5000px; margin-top: -5000px; position: relative; } #play-region { height: 15px; } #timetick { height: 35px; width: 100%; z-index: 100; position: relative; } #timetick .seconds { height: 35px; border-left: solid 1px #737272; box-sizing: border-box; padding-top: 10px; padding-left: 2px; float: left; } .layer { height: 20px; overflow: hidden; margin-bottom: 1px; } .frame { height: 20px; border-right: dotted 1px rgba(0, 0, 0, 0.6); display: inline-block; box-sizing: border-box; vertical-align: top; border-radius: 2px; border-bottom: 1px solid rgba(0, 0, 0, 0.3); } .frame-selected { border-bottom: 4px solid rgba(255, 255, 255, 0.8); } .frame-button { width: 20px; height: 20px; margin-left: 1px; display: inline-block; vertical-align: top; border-radius: 20px; text-align: center; fill: rgba(255, 255, 255, 0.5); } .layer-disabled { opacity: 0.5; } .dur-handle, .roll-handle { width: 10px; height: 10px; cursor: col-resize; fill: rgba(255, 255, 255, 0.5); z-index: 100; /* opacity: 0; */ } .dur-handle { float: right; cursor: ew-resize; } .dur-handle:first-of-type { margin-top: 10px; } .new-frame { width: 20px; height: 20px; fill: rgba(255, 255, 255, 0.5); z-index: 101; position: relative; padding-top: 5px; box-sizing: border-box; cursor: copy; } #timeline-settings { width: 80px; text-align: center; float: left; } #timecode { background-color: transparent; border: 0px solid; font-family: 'Source Code Pro', monospace; font-size: 14px; color: #3bc6de; margin: 2px; width: 70px; text-align: center; } #timecode:hover { color: #dfdfdf; } #timecode:focus { background-color: #e9e9e9; color: #6a6a6a; } textarea:focus, input:focus { outline: none; } #frame-rate { width: 80px; font-size: 10px; text-align: center; color: #3bc6de; } .gridlines { background-color: transparent; height: 10000px; margin-top: -10000px; background-size: 100% 100%; } </style> </head> <body translate="no"> <div id="app"> <div id="timeline-header"></div> <div id="switch-header" v-bind:style="{width : switchWidth + 'px'}"> <!-- <button v-on:click="spaceDown()">drag</button> <button v-on:click="spaceUp()">release</button> --> <div class="av-features"> <div class="icon-ref">👁</div> <div class="icon-ref">⚪</div> <div class="icon-ref">🔒</div> </div> <div id="timeline-settings"> <input id="timecode" v-model.number="frameTime" @focus="$event.target.select()"> {{frameRate}}fps </div> <button v-on:click="newLayer()">New Layer</button> <!-- <button v-on:click="timelineZoomIn()">+</button> <button v-on:click="timelineZoomOut()">-</button> --> </div> <div id="content" v-bind:class="{ dragscroll : spaceDrag }"> <div id="switches" v-bind:style="{width : switchWidth + 'px'}"> <!-- <div class="layer-info" v-for="num in 100"> {{num}} </div> --> <!-- <draggable v-model="timeline.layers" :options="{}" @start="drag=true"> --> <draggable v-model="timeline.layers" :options="{}" @start="drag=true" @end="reorderLayer"> <div class="layer-info" v-for="(layer, layerIndex) in timeline.layers" v-on:click="sel_group(layerIndex)" v-bind:class="{'group-selected' : layerIndex == selectedLayerGroup, 'unlocked' : !layer.locked}" v-bind:id="layerIndex"> <div class="av-features"> <button v-on:click="toggleLayerVisibility(layer, layerIndex)"> {{!layer.visible ? ' ' : '👁'}}</button> <button v-on:click="toggleLayerSolo(layer, layerIndex)"> {{!layer.solo ? ' ' : '⚪'}}</button> <button v-on:click="toggleLayerLock(layer, layerIndex)"> {{!layer.locked ? ' ' : '🔒'}}</button> </div> <div class="index">{{layerIndex + 1}}</div> <div class="label-color"> <button v-bind:style="{ 'background-color': labelColors[layer.label]}"></button> </div> <div class="layer-name">{{layer.name}}</div> <!-- <input type="text" v-model="layer.name"> --> <!-- <div class="layer-name" @dblclick = "layer.edit = true">{{layer.name}}</div> <input v-show = "layer.edit == true" v-model = "layer.name" v-on:blur= "layer.edit=false; $emit('update')" @keyup.enter = "layer.edit=false; $emit('update')"> --> </div> </draggable> <!-- <div class="layer-info"> <div class="label-color" v-for="(label, labelIndex) in labelColors"> <button v-on:click="layer_label(selectedLayerGroup, labelIndex)" v-bind:style="{ 'background-color': labelColors[labelIndex]}"></button> </div> </div> --> <div class="layer-filler" v-bind:style="{'padding-left' : '4px', 'height': (timelineHeight - timeline.layers.length*21) + 'px'}"> - : zoom out <br> + : zoom in <br> → : forward 1 frame <br> ← : back 1 frame <br> ⇪→ : forward 10 frames <br> ⇪← : back 10 frames <br> ↓ : next frame <br> ↑ : last frame <br> space : drag timeline <br> S : split frame <br> </div> </div> <div id="timeline-scroll" v-bind:class="{ dragscroll : spaceDrag }" v-bind:style="{width : timelineWidth-4 + 'px', 'margin-left' : switchWidth + 'px'}"> <div id="timeline" v-bind:style="{'width' : zoom + '%'}"> <div id="timetick"> <div class="seconds" v-for="(second, index) in timeline.duration" v-bind:style="{ width: (1/timeline.duration*100) + '%' }"> {{index}}s </div> </div> <div id="playhead-icon" v-bind:style="{left : frameTime * gridPercent + '%'}"> <svg width="10" height="14"><g fill="none" fill-rule="evenodd"><path fill="#8BCCD6" d="M0 0h10v9l-5 5-5-5z"/><path fill="#9ED8E0" d="M5 0h5v9l-5 5z"/></g></svg> </div> <div id="playhead-handle" v-bind:style="{left : frameTime * gridPercent + '%'}"></div> <div id="play-region"></div> <!-- <div class="layer" v-for="layer in 50"> <div class="frame" v-for="frame in 14" v-bind:style="{ 'background-color': labelColors[layer % 16 + 1], width: Math.ceil(Math.random()*10) * gridPercent + '%'}"></div> </div> --> <div class="layer" v-for="(layer, layerIndex) in timeline.layers" v-bind:id="layerIndex"> <draggable v-model="layer.frames" @start="drag=true" @end="reorderFrame" v-bind:id="layerIndex"> <div class="frame" v-for="(frame, frameIndex) in layer.frames" v-on:click="sel_frame(layerIndex, frameIndex)" v-bind:class="{'frame-selected' : layerIndex == selectedLayerGroup && frameIndex == selectedFrame, 'layer-disabled' : !layer.visible}" v-bind:id="frameIndex" v-bind:style="{ 'background-color': labelColors[layer.label], width: frame.dur * gridPercent + '%'}"> <div class="roll-handle" v-if="frameIndex != 0" v-show="!layer.locked && layer.visible && (frame.dur * gridWidth) > 12"> <svg width="20" height="20" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M1.676 7.5C1.258 7.164.5 5.925.5 5A4.5 4.5 0 0 1 5 .5h5v2H8.39C8.916 2.845 9.5 4.075 9.5 5A4.5 4.5 0 0 1 5 9.5H0v-2h1.676zM5 7.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/></svg> </div> <div class="dur-handle" v-show="!layer.locked && layer.visible && (frame.dur * gridWidth) > 12"> <svg width="20" height="20" viewBox="0 0 20 20"><path d="M10 0v10H0z" fill-rule="evenodd"/></svg> </div></draggable> </div><div class="frame-button" v-show="!layer.locked && layer.visible" v-bind:style="{'color':'#fff', 'background-color': labelColors[layer.label]}" v-on:click="newFrame(layerIndex)"> <div class="new-frame"> <svg width="10" height="10"><path d="M6.5 3.5H10v3H6.5V10h-3V6.5H0v-3h3.5V0h3z" fill-rule="evenodd"/></svg> </div> </div> </div> <div class="layer-filler" v-bind:style="{'height': (timelineHeight - timeline.layers.length*21) + 'px'}"></div> <div id="playhead" v-bind:style="{width : gridPercent + '%', left : frameTime * gridPercent + '%'}"></div> <div class="gridlines" v-bind:style="{'background-size': (1/timeline.duration*100) + '% 100%', 'background-image': cssGrid}"></div> </div> </div> <div class="label-popup" v-show="labelPopup"> <div class="label-picker" v-for="(label, labelIndex) in labelColors"> <button v-on:click="layer_label(selectedLayerGroup, labelIndex)" v-bind:style="{ 'background-color': labelColors[labelIndex]}"></button.........完整代码请登录后点击上方下载按钮下载查看
网友评论0