parent
eb68c1e5c9
commit
eaad8a51c2
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,406 @@ |
||||
<template> |
||||
<v-card class="fill-height align-center"> |
||||
<v-dialog v-model="fileSystem"> |
||||
<v-card> |
||||
<v-card-title>File System</v-card-title> |
||||
<v-card-text> |
||||
<v-file-input label="Upload" id="ufile"></v-file-input><v-btn @click="uploadfile">upload</v-btn> |
||||
<v-row> |
||||
<v-col cols="12" md="2" v-for="file in files" :key="file"> |
||||
<v-card> |
||||
{{file}} |
||||
</v-card> |
||||
</v-col> |
||||
</v-row> |
||||
</v-card-text> |
||||
</v-card> |
||||
</v-dialog> |
||||
<v-dialog v-model="createSceneModel" max-width="300px"> |
||||
<v-card> |
||||
<v-card-title>New Scene</v-card-title> |
||||
<v-card-text> |
||||
<v-text-field v-model="sceneName" label="Scene Name"></v-text-field> |
||||
</v-card-text> |
||||
<v-card-actions> |
||||
<v-btn text @click="createSceneModel=false">Cancel</v-btn> |
||||
<v-spacer></v-spacer> |
||||
<v-btn text color="primary" @click="createScene">Save</v-btn> |
||||
</v-card-actions> |
||||
</v-card> |
||||
</v-dialog> |
||||
<v-dialog v-model="createSceneItem" max-width="300px"> |
||||
<v-card> |
||||
<v-card-title>New Source</v-card-title> |
||||
<v-card-text> |
||||
<v-text-field v-model="sourceName" label="Source Name"></v-text-field> |
||||
<v-select v-model="sourceType" item-value="value" item-text="name" :items="[{name:'Image', value:'image_source'},{name: 'VLC Source', value: 'vlc_source'},{name: 'Media Source', value: 'ffmpeg_source'},{name: 'Text', value: 'text_ft2_source_v2'}]" label="Source Type"></v-select> |
||||
<v-text-field label="Image Path" v-model="sourceSettings.file" v-if="sourceType === 'image_source'"></v-text-field> |
||||
<v-text-field label="File Path" v-model="sourceSettings.localFile" v-if="sourceType === 'ffmpeg_source'"></v-text-field> |
||||
<div v-if="sourceType === 'vlc_source'"> |
||||
<div v-for="(file, index) in sourceSettings.playlist" :key="index"> |
||||
{{file}} |
||||
</div> |
||||
</div> |
||||
<v-text-field v-if="sourceType === 'vlc_source'" v-model="sourceSettings.file"></v-text-field><v-btn small text @click="addPlaylist(sourceSettings.file)">+</v-btn> |
||||
</v-card-text> |
||||
<v-card-actions> |
||||
<v-btn text @click="createSceneItem=false">Cancel</v-btn> |
||||
<v-spacer></v-spacer> |
||||
<v-btn text color="primary" @click="createSource">Save</v-btn> |
||||
</v-card-actions> |
||||
</v-card> |
||||
</v-dialog> |
||||
<v-card-text> |
||||
<v-row> |
||||
<v-col cols="12" md="9"> |
||||
<v-card ref="mainvid" id="mainvid" |
||||
:style="`font-size:10px;height: ${vidheight}px;background: #ccc;position: relative`" |
||||
v-if="selecteScene"> |
||||
<div :style="`border:1px solid #ccc;width:${source.cx*vidperm}px;z-index:${selecteScene.sources.length - index};height:${source.cy*vidperm}px;background-size: 100% 100%;top:${source.y*vidperm}px;left:${source.x*vidperm}px;position:absolute;min-width:50px;min-height:50px;background:url()`" |
||||
v-for="(source,index) in selecteScene.sources" :key="index"> |
||||
<div style="top:0;left:0;position: absolute;color:#fff;text-shadow: #000 1px 1px"> |
||||
{{source.name}} ({{source.id}}) |
||||
</div> |
||||
<div style="top:0;right:0;position: absolute;color:#fff;text-shadow: #000 1px 1px"> |
||||
{{source.type}} ({{source.source_cx}} x {{source.source_cy}} ) |
||||
</div> |
||||
<img v-if="source.srcdata" width="100%" height="100%" :src="source.srcdata"/> |
||||
</div> |
||||
</v-card> |
||||
</v-col> |
||||
<v-col cols="12" md="3"> |
||||
<v-card v-if="selectedItemProps"> |
||||
<v-card-text> |
||||
<v-text-field label="Name" v-model="selectedItemProps.name"></v-text-field> |
||||
<v-text-field :label="`Width (Source: ${selectedItemProps.sourceWidth})`" |
||||
v-model="selectedItemProps.width" @keyup="selectedItemProps.height=selectedItemProps.width * (selectedItemProps.sourceHeight/selectedItemProps.sourceWidth)"></v-text-field> |
||||
<v-text-field :label="`Height (Source: ${selectedItemProps.sourceHeight})`" |
||||
v-model="selectedItemProps.height"></v-text-field> |
||||
</v-card-text> |
||||
<v-card-actions> |
||||
<v-spacer></v-spacer> |
||||
<v-btn small @click="saveSelectedItem">Save</v-btn> |
||||
</v-card-actions> |
||||
</v-card> |
||||
<br/> |
||||
<v-card v-if="selectedItemSourceProps"> |
||||
<v-card-title>Source Options |
||||
<v-spacer></v-spacer> |
||||
<v-btn icon x-smal @click="panels.source = !panels.source"> |
||||
<v-icon>mdi-menu</v-icon> |
||||
</v-btn> |
||||
</v-card-title> |
||||
<v-card-text v-show="panels.source"> |
||||
<v-text-field v-if="selectedItemSourceProps.sourceType === 'xcomposite_input'" |
||||
label="Capture Window" |
||||
v-model="selectedItemSourceProps.sourceSettings.capture_window"></v-text-field> |
||||
<v-text-field v-if="selectedItemSourceProps.sourceType === 'image_source'" label="File" |
||||
v-model="selectedItemSourceProps.sourceSettings.file"></v-text-field> |
||||
<div v-if="selectedItemSourceProps.sourceType === 'vlc_source'"> |
||||
<div v-for="(item, index) in selectedItemSourceProps.sourceSettings.playlist" |
||||
:key="index"> |
||||
<v-row> |
||||
<v-col cols="12" md="10">{{item.value}}</v-col> |
||||
<v-col cols="12" md="2"> |
||||
<v-btn icon small |
||||
@click="selectedItemSourceProps.sourceSettings.playlist.splice(index,1)"> |
||||
<v-icon>mdi-delete</v-icon> |
||||
</v-btn> |
||||
</v-col> |
||||
</v-row> |
||||
</div> |
||||
<v-text-field v-if="selectedItemSourceProps.sourceType === 'vlc_source'" label="File" |
||||
v-model="selectedItemSourceProps.file"></v-text-field> |
||||
|
||||
<v-spacer></v-spacer> |
||||
<v-btn small |
||||
@click="selectedItemSourceProps.sourceSettings.playlist.push({ hidden: false, selected: false, value: selectedItemSourceProps.file}); selectedItemSourceProps.file=''"> |
||||
Add To Playlist |
||||
</v-btn> |
||||
|
||||
</div> |
||||
</v-card-text> |
||||
<v-card-actions v-show="panels.source"> |
||||
<v-spacer></v-spacer> |
||||
<v-btn small>Save</v-btn> |
||||
</v-card-actions> |
||||
</v-card> |
||||
<br/> |
||||
<v-card v-if="selectedItemProps"> |
||||
<v-card-title>Position |
||||
<v-spacer></v-spacer> |
||||
<v-btn icon x-smal @click="panels.position = !panels.position" l> |
||||
<v-icon>mdi-menu</v-icon> |
||||
</v-btn> |
||||
</v-card-title> |
||||
<v-card-text v-show="panels.position"> |
||||
<v-text-field label="X" v-model="selectedItemProps.position.x"></v-text-field> |
||||
<v-text-field label="Y" v-model="selectedItemProps.position.y"></v-text-field> |
||||
<v-text-field label="Rotation" v-model="selectedItemProps.rotation"></v-text-field> |
||||
</v-card-text> |
||||
<v-card-actions v-show="panels.position"> |
||||
<v-spacer></v-spacer> |
||||
<v-btn small>Save</v-btn> |
||||
</v-card-actions> |
||||
|
||||
</v-card> |
||||
<br/> |
||||
|
||||
<v-card v-if="selectedItemProps"> |
||||
<v-card-title>Crop |
||||
<v-spacer></v-spacer> |
||||
<v-btn icon x-smal @click="panels.crop = !panels.crop"> |
||||
<v-icon>mdi-menu</v-icon> |
||||
</v-btn> |
||||
</v-card-title> |
||||
<v-card-text v-show="panels.crop"> |
||||
<v-text-field label="Top" v-model="selectedItemProps.crop.top"></v-text-field> |
||||
<v-text-field label="Left" v-model="selectedItemProps.crop.left"></v-text-field> |
||||
<v-text-field label="Bottom" v-model="selectedItemProps.crop.bottom"></v-text-field> |
||||
<v-text-field label="Right" v-model="selectedItemProps.crop.right"></v-text-field> |
||||
</v-card-text> |
||||
<v-card-actions v-show="panels.crop"> |
||||
<v-spacer></v-spacer> |
||||
<v-btn small>Save</v-btn> |
||||
</v-card-actions> |
||||
|
||||
</v-card> |
||||
</v-col> |
||||
</v-row> |
||||
<v-row> |
||||
<v-col cols="12" md="3"> |
||||
<v-list> |
||||
<v-list-item-group v-model="selecteSceneName" color="primary"> |
||||
|
||||
<v-list-item v-for="(scene,index) in scenes" :key="index" @click="setSelect(scene)"> |
||||
<v-list-item-content> |
||||
<v-list-item-title v-text="scene.name"></v-list-item-title> |
||||
</v-list-item-content> |
||||
</v-list-item> |
||||
<v-list-item> |
||||
<v-list-item-content> |
||||
<v-list-item-title @click="createSceneModel=true">New Scene</v-list-item-title> |
||||
</v-list-item-content> |
||||
</v-list-item> |
||||
</v-list-item-group> |
||||
</v-list> |
||||
</v-col> |
||||
|
||||
<v-col cols="12" md="3"> |
||||
<v-list v-if="selecteScene"> |
||||
<v-list-item v-for="(source,index) in selecteScene.sources" :key="index" |
||||
@click="setItemSelect(source)"> |
||||
<v-list-item-content> |
||||
<v-list-item-title v-text="source.name"></v-list-item-title> |
||||
</v-list-item-content> |
||||
</v-list-item> |
||||
<v-list-item> |
||||
<v-list-item-content> |
||||
<v-list-item-title @click="createSceneItem=true">New Item</v-list-item-title> |
||||
</v-list-item-content> |
||||
</v-list-item> |
||||
</v-list> |
||||
</v-col> |
||||
</v-row> |
||||
</v-card-text> |
||||
</v-card> |
||||
</template> |
||||
|
||||
<script> |
||||
const OBSWebSocket = require('obs-websocket-js'); |
||||
|
||||
export default { |
||||
name: "ObsVue", |
||||
props: { |
||||
server: String |
||||
}, |
||||
data: () => ({ |
||||
obs: null, |
||||
selectedItemProps: null, |
||||
selectedItemSourceProps: null, |
||||
createSceneModel: false, |
||||
createSceneItem: false, |
||||
sourceSettings: { |
||||
playlist: [], |
||||
file: '', |
||||
localFile: '' |
||||
}, |
||||
files: [ |
||||
'test.jpg', |
||||
'test.png' |
||||
], |
||||
sourceType: '', |
||||
sourceName: '', |
||||
scenes: [], |
||||
sources: [], |
||||
panels: { |
||||
source: false, |
||||
position: false, |
||||
crop: false |
||||
}, |
||||
previewInterval: 0, |
||||
selecteScene: null, |
||||
sceneName: '', |
||||
vidheight: 0, |
||||
vidperm: 0, |
||||
selecteSceneName: null, |
||||
fileSystem: true |
||||
|
||||
}), |
||||
mounted() { |
||||
this.obs = new OBSWebSocket() |
||||
this.getfiles() |
||||
this.obs.connect({address: 'ws://localhost:4444', password: 'neverdie'}).then(this.connected) |
||||
this.obs.on('error', err => { |
||||
console.error('socket error:', err); |
||||
}); |
||||
}, |
||||
methods: { |
||||
uploadfile() { |
||||
var file = document.getElementById('ufile') |
||||
console.log(file.files[0]) |
||||
var filereader = new FileReader() |
||||
filereader.onloadend = () => { |
||||
this.$axios.post('http://localhost:5000/api/upload', {"file": filereader.result.split(';base64,')[1], 'file_name': file.files[0].name}).then(this.getfiles) |
||||
} |
||||
|
||||
filereader.readAsDataURL(file.files[0]); |
||||
}, |
||||
getfiles () { |
||||
this.$axios.get('http://localhost:5000/api/files').then(result => { |
||||
this.files = result.data |
||||
}) |
||||
}, |
||||
addPlaylist(item) { |
||||
this.sourceSettings.playlist.push({hidden: false, selected: false, value:item}) |
||||
console.log(this.sourceSettings) |
||||
}, |
||||
createScene() { |
||||
this.obs.send('CreateNewScene', {'sceneName': this.sceneName}).then(response => { |
||||
console.log(response) |
||||
this.sceneName = '' |
||||
this.createSceneModel = false |
||||
this.sceneList() |
||||
}) |
||||
}, |
||||
createSource() { |
||||
var sourceSetting; |
||||
if (this.sourceType === 'vlc_source') sourceSetting = {playlist: this.sourceSettings.playlist} |
||||
if (this.sourceType === 'ffmpeg_source') sourceSetting = {localfile: this.sourceSettings.localfile} |
||||
if (this.sourceType === 'image_source') sourceSetting = {file: this.sourceSettings.file} |
||||
console.log({sourceName: this.sourceName,sourceSettings: sourceSetting, sourceType: this.sourceType }) |
||||
this.obs.send('CreateNewSource', {sourceName: this.sourceName,sourceSettings: sourceSetting, sourceType: this.sourceType }).then(response => { |
||||
console.log(response) |
||||
this.sceneName = '' |
||||
this.createSceneModel = false |
||||
this.obs.send('AddToScene', {sourceName:this.sourceName }) |
||||
this.sceneList() |
||||
}) |
||||
}, |
||||
onAuth() { |
||||
console.log("auth") |
||||
}, |
||||
newScene() { |
||||
|
||||
}, |
||||
setItemSelect(item) { |
||||
this.obs.send("GetSceneItemProperties", { |
||||
item: item.name, |
||||
"scene-name": this.selecteScene.name |
||||
}).then(data => { |
||||
console.log("GetSceneItemProperties") |
||||
console.log(data) |
||||
this.selectedItemProps = data |
||||
}) |
||||
this.obs.send("GetSourceSettings", {sourceName: item.name}).then(data => { |
||||
console.log("GetSourceSettings") |
||||
console.log(data) |
||||
this.selectedItemSourceProps = data |
||||
}) |
||||
}, |
||||
setSelect(item) { |
||||
console.log(item) |
||||
this.selecteScene = item |
||||
this.selecteSceneName = item.name |
||||
this.obs.send('GetSourcesList').then(data => { |
||||
console.log('GetSourcesList') |
||||
console.log(data) |
||||
}) |
||||
|
||||
if (this.previewInterval > 0) clearInterval(this.previewInterval) |
||||
this.previewInterval = setInterval(() => { |
||||
this.selecteScene.sources.forEach(sitem => { |
||||
this.obs.send('TakeSourceScreenshot', { |
||||
sourceName: sitem.name, |
||||
embedPictureFormat: 'png' |
||||
}).then(data => { |
||||
sitem.srcdata = data.img |
||||
this.selecteScene.__ob__.dep.notify() |
||||
this.vidheight = document.getElementById('mainvid').offsetWidth / (16 / 9) |
||||
this.vidperm = this.vidheight / 1080.0 |
||||
}).catch(err => { |
||||
console.log(err) |
||||
}) |
||||
}) |
||||
}, 500) |
||||
|
||||
}, |
||||
saveSelectedItem() { |
||||
console.log( { |
||||
'item': this.selectedItemProps.name, |
||||
position: { |
||||
x: parseInt(this.selectedItemProps.position.x), |
||||
y: parseInt(this.selectedItemProps.position.y), |
||||
}, |
||||
crop: this.selectedItemProps.crop, |
||||
rotation: this.selectedItemProps.rotation, |
||||
height: this.selectedItemProps.height, |
||||
scale: {x:this.selectedItemProps.width/this.selectedItemProps.sourceWidth,y:this.selectedItemProps.height/this.selectedItemProps.sourceHeight} |
||||
}) |
||||
this.obs.send('SetSceneItemProperties', { |
||||
item: this.selectedItemProps.name, |
||||
position: { |
||||
x: parseInt(this.selectedItemProps.position.x), |
||||
y: parseInt(this.selectedItemProps.position.y), |
||||
}, |
||||
crop: { |
||||
top:parseInt(this.selectedItemProps.crop.top), |
||||
bottom:parseInt(this.selectedItemProps.crop.bottom), |
||||
left:parseInt(this.selectedItemProps.crop.left), |
||||
right:parseInt(this.selectedItemProps.crop.right), |
||||
}, |
||||
rotation: this.selectedItemProps.rotation, |
||||
height: this.selectedItemProps.height, |
||||
scale: {x:this.selectedItemProps.width/this.selectedItemProps.sourceWidth,y:this.selectedItemProps.height/this.selectedItemProps.sourceHeight} |
||||
}).then(data => { |
||||
console.log(data) |
||||
this.sceneList() |
||||
}) |
||||
}, |
||||
sceneList() { |
||||
this.obs.send('GetSceneList').then(data => { |
||||
console.log("scene", data) |
||||
this.scenes = data.scenes |
||||
data.scenes.forEach(item => { |
||||
if (item.name === data["current-scene"]) { |
||||
this.setSelect(item) |
||||
} |
||||
}) |
||||
}) |
||||
}, |
||||
connected() { |
||||
this.obs.send('GetVersion').then(data => { |
||||
this.obs.send('GetVersion').then(data2 => { |
||||
console.log(data) |
||||
console.log(data2) |
||||
}) |
||||
}) |
||||
this.sceneList() |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,112 @@ |
||||
<template> |
||||
<RemoteFeed v-if="remoteStream" :single-view="true" :opaqueId="opaqueId" :mypvtid="mypvtid" :feedid="remoteStream.id" :remote-stream="remoteStream" :janusInit="janusInit" :room="1234" ></RemoteFeed> |
||||
</template> |
||||
|
||||
<script> |
||||
import RemoteFeed from "@/lib/RemoteFeed"; |
||||
export default { |
||||
name: "RemoteFeedOnly", |
||||
props: { |
||||
server: String, |
||||
room: Number, |
||||
user: String |
||||
}, |
||||
components: {RemoteFeed}, |
||||
data: () => ({ |
||||
remoteStream: null, |
||||
opaqueId: null, |
||||
mypvtid: null, |
||||
janusInit: null, |
||||
run: false |
||||
}), |
||||
mounted() { |
||||
this.janusInit = new this.$janus({ |
||||
success: this.janusSuccess, |
||||
server: this.server, |
||||
error: function (error) { |
||||
this.$janus.error(error); |
||||
}, |
||||
//webrtcState: this.webrtcState, |
||||
destroyed: function () { |
||||
window.location.reload(); |
||||
} |
||||
}) |
||||
|
||||
}, |
||||
methods: { |
||||
janusMessage(msg, jsep) { |
||||
console.log(msg); |
||||
var event = msg["videoroom"]; |
||||
|
||||
if (event !== undefined) { |
||||
|
||||
if (event === "joined") { |
||||
this.myid = msg["id"]; |
||||
this.mypvtid = msg["private_id"]; |
||||
this.$janus.log("Successfully joined room " + msg["room"] + " with ID " + this.myid); |
||||
// this.restartDevices() |
||||
if (msg["publishers"] !== undefined && msg["publishers"] !== null) { |
||||
msg['publishers'].forEach(publisher => { |
||||
console.log(publisher.id) |
||||
console.log(publisher) |
||||
if (publisher.display === this.user) { |
||||
this.remoteStream = publisher |
||||
this.run = true |
||||
} |
||||
}); |
||||
} |
||||
} else if (event === "event") { |
||||
if (msg["publishers"] !== undefined && msg["publishers"] !== null) { |
||||
msg['publishers'].forEach(publisher => { |
||||
console.log(publisher.id) |
||||
console.log(publisher) |
||||
if (publisher.display === this.user) { |
||||
this.remoteStream = publisher |
||||
this.run = true |
||||
} |
||||
}); |
||||
|
||||
} else if (msg["leaving"] !== undefined && msg["leaving"] !== null) { |
||||
console.log("leaving") |
||||
} |
||||
} |
||||
} |
||||
if (jsep !== undefined && jsep !== null) { |
||||
|
||||
console.log("Handling SDP as well..."); |
||||
console.log(jsep); |
||||
this.pluginHandle.handleRemoteJsep({jsep: jsep}); |
||||
var audio = msg["audio_codec"]; |
||||
if (this.mystream && this.mystream.getAudioTracks() && this.mystream.getAudioTracks().length > 0 && !audio) { |
||||
console.log("Our audio stream has been rejected, viewers won't hear us"); |
||||
} |
||||
} |
||||
}, |
||||
janusPluginSuccess (pluginHandle) { |
||||
this.pluginHandle = pluginHandle |
||||
this.$janus.log("Plugin attached! (" + this.pluginHandle.getPlugin() + ", id=" + this.pluginHandle.getId() + ")"); |
||||
this.$janus.log(" -- This is a publisher/manager"); |
||||
|
||||
var register = { "request": "join", "room": this.room, "ptype": "publisher" }; |
||||
pluginHandle.send({"message": register}); |
||||
}, |
||||
janusSuccess() { |
||||
this.opaqueId = "videoroomtest-" + this.$janus.randomString(12); |
||||
|
||||
this.janusInit.attach( |
||||
{ |
||||
plugin: "janus.plugin.videoroom", |
||||
opaqueId: this.opaqueId, |
||||
success: this.janusPluginSuccess, |
||||
error: this.janusPluginError, |
||||
onmessage: this.janusMessage, |
||||
} |
||||
) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,720 @@ |
||||
<template> |
||||
<v-container> |
||||
<v-row class="fill-height"> |
||||
<v-col cols="24" md="1"> |
||||
<v-card width="100%" dark class="fill-height"> |
||||
<v-card-text> |
||||
<v-row> |
||||
<v-col> |
||||
<v-icon @click="addItemStart('text')">mdi-format-color-text</v-icon> |
||||
</v-col> |
||||
<v-col> |
||||
<v-icon @mousedown="addItemStart('rect')" @mouseup="clearStart()"> |
||||
mdi-rectangle-outline |
||||
</v-icon> |
||||
</v-col> |
||||
<v-col> |
||||
<v-icon @mousedown="addItemStart('image')" @mouseup="clearStart()">mdi-image</v-icon> |
||||
</v-col> |
||||
</v-row> |
||||
</v-card-text> |
||||
</v-card> |
||||
</v-col> |
||||
<v-col cols="24" md="9"> |
||||
<v-card width="100%" min-height="600" dark class="fill-height"> |
||||
<v-card-text color="white"> |
||||
<div ref="vid" @click="handleAction" style="width: 100%;background: #fff;position: relative"> |
||||
<div v-for="(track, index) in mainTracks" :key="index"> |
||||
<div v-for="(keyframe, index) in track.keyframes" :key="index"> |
||||
<div @click="selectTrack(track)" |
||||
v-if="keyframe.start <= currentTime && keyframe.end >=currentTime && track.type === 'text'" |
||||
@mouseenter="colorBorder(track)" |
||||
@mouseleave="colorBorderClear(track)" @mousedown="itemMoveStart" |
||||
@mouseup="itemMoveEnd" :ref="'item'+track.id" @dblclick="editText(track)" |
||||
:style="`padding:4px;color:${track.color};position:absolute;opacity:${vidTracks[track.id].opacity};top:${vidTracks[track.id].y-4}px;left:${vidTracks[track.id].x-4}px;font-size:${track.size};font-family:${track.font};border: 1px solid ${selectedTrack?(selectedTrack.id === track.id?'#ff0000':'transparent'):'transparent'}`" |
||||
> |
||||
<span :style="`line-height:${vidTracks[track.id].lineHeight}!important;letter-spacing:${vidTracks[track.id].letterSpacing}!important;`" v-html="track.text.replace(/\n/g, '<br>')"></span> |
||||
<div></div> |
||||
</div> |
||||
<div @click="selectTrack(track)" @mouseenter="colorBorder(track)" |
||||
v-if="keyframe.start <= currentTime && keyframe.end >=currentTime && track.type === 'image'" |
||||
@mouseleave="colorBorderClear(track)" @mousedown="itemMoveStart" |
||||
@mouseup="itemMoveEnd" :ref="'item'+track.id" @dblclick="editText(track)" |
||||
:style="`padding:4px;color:${track.color};position:absolute;opacity:${vidTracks[track.id].opacity};top:${vidTracks[track.id].y-4}px;left:${vidTracks[track.id].x-4}px;font-size:${track.size};font-family:${track.font};border: 1px solid ${selectedTrack?(selectedTrack.id === track.id?'#ff0000':'transparent'):'transparent'}`" |
||||
> |
||||
<img :src="track.image_data" |
||||
:width="vidTracks[track.id].width>0?vidTracks[track.id].width+'px':''" |
||||
:height="vidTracks[track.id].height>0?vidTracks[track.id].height+'px':''"> |
||||
<div></div> |
||||
</div> |
||||
<div @click="selectTrack(track)" @mouseenter="colorBorder(track)" |
||||
v-if="keyframe.start <= currentTime && keyframe.end >=currentTime && track.type === 'rect'" |
||||
@mouseleave="colorBorderClear(track)" @mousedown="itemMoveStart" |
||||
@mouseup="itemMoveEnd" :ref="'item'+track.id" @dblclick="editText(track)" |
||||
:style="`padding:4px;color:${track.color};position:absolute;opacity:${vidTracks[track.id].opacity};top:${vidTracks[track.id].y-4}px;left:${vidTracks[track.id].x-4}px;font-size:${track.size};font-family:${track.font};border: 1px solid ${selectedTrack?(selectedTrack.id === track.id?'#ff0000':'transparent'):'transparent'}`" |
||||
> |
||||
<div :style="`width:${vidTracks[track.id].width}px;height:${vidTracks[track.id].height}px;background-color:${track.color}`"></div> |
||||
</div> |
||||
|
||||
</div> |
||||
|
||||
</div> |
||||
</div> |
||||
</v-card-text> |
||||
</v-card> |
||||
</v-col> |
||||
<v-col cols="24" md="2"> |
||||
<v-card width="100%" dark class="fill-height"> |
||||
<v-card-text> |
||||
<v-row> |
||||
<v-col v-if="selectedTrack"> |
||||
<v-text-field v-if="selectedTrack.type === 'text'" label="Font Size" |
||||
placeholder="Font Size" |
||||
v-model="mainTracks[currentKeyTrack.track].size" |
||||
@keyup="mainTracks.__ob__.dep.notify()"></v-text-field> |
||||
<v-textarea v-if="selectedTrack.type === 'text'" label="Text" |
||||
@keyup="mainTracks.__ob__.dep.notify()" placeholder="Text" |
||||
v-model="mainTracks[currentKeyTrack.track].text"></v-textarea> |
||||
<v-text-field v-if="selectedTrack.type !== 'text'" label="Name" |
||||
@keyup="mainTracks.__ob__.dep.notify()" placeholder="Name" |
||||
v-model="mainTracks[currentKeyTrack.track].text"></v-text-field> |
||||
|
||||
<v-menu |
||||
ref="menu" |
||||
v-model="colorMenu" |
||||
:close-on-content-click="false" |
||||
|
||||
transition="scale-transition" |
||||
offset-y |
||||
min-width="290px" |
||||
v-if="selectedTrack.type !== 'image'" |
||||
> |
||||
<template v-slot:activator="{ on }"> |
||||
<v-text-field |
||||
v-model="mainTracks[currentKeyTrack.track].color" |
||||
label="Color" |
||||
readonly |
||||
v-on="on" |
||||
></v-text-field> |
||||
</template> |
||||
<v-color-picker v-model="mainTracks[currentKeyTrack.track].color" no-title |
||||
scrollable show-swatches> |
||||
<v-spacer></v-spacer> |
||||
</v-color-picker> |
||||
</v-menu> |
||||
<v-text-field v-if="selectedTrack.type !== 'text'" label="Width" |
||||
@keyup="mainTracks.__ob__.dep.notify()" placeholder="Width" |
||||
v-model="mainTracks[currentKeyTrack.track].keyframes[currentKeyTrack.keyframe].width"></v-text-field> |
||||
|
||||
<v-text-field v-if="selectedTrack.type === 'text'" label="Line Height" |
||||
@keyup="mainTracks.__ob__.dep.notify()" placeholder="Line Height" |
||||
v-model="mainTracks[currentKeyTrack.track].keyframes[currentKeyTrack.keyframe].lineHeight"></v-text-field> |
||||
<v-text-field v-if="selectedTrack.type === 'text'" label="Letter Spacing" |
||||
@keyup="mainTracks.__ob__.dep.notify()" placeholder="Letter Spacing" |
||||
v-model="mainTracks[currentKeyTrack.track].keyframes[currentKeyTrack.keyframe].letterSpacing"></v-text-field> |
||||
<v-text-field v-if="selectedTrack.type !== 'text'" label="Height" |
||||
@keyup="mainTracks.__ob__.dep.notify()" placeholder="Height" |
||||
v-model="mainTracks[currentKeyTrack.track].keyframes[currentKeyTrack.keyframe].height"></v-text-field> |
||||
<v-text-field v-if="selectedTrack.type === 'text'" label="Font" |
||||
@keyup="mainTracks.__ob__.dep.notify()" placeholder="Text" |
||||
v-model="mainTracks[currentKeyTrack.track].font"></v-text-field> |
||||
<v-text-field label="X" @keyup="mainTracks.__ob__.dep.notify()" placeholder="X" |
||||
v-model="mainTracks[currentKeyTrack.track].keyframes[currentKeyTrack.keyframe].x"></v-text-field> |
||||
<v-text-field label="Y" @keyup="mainTracks.__ob__.dep.notify()" placeholder="Y" |
||||
v-model="mainTracks[currentKeyTrack.track].keyframes[currentKeyTrack.keyframe].y"></v-text-field> |
||||
<v-text-field label="Opacity" @keyup="mainTracks.__ob__.dep.notify()" |
||||
placeholder="Opacity" |
||||
v-model="mainTracks[currentKeyTrack.track].keyframes[currentKeyTrack.keyframe].opacity"></v-text-field> |
||||
<v-btn small text @click="newKeyFrame">new keyframe</v-btn> |
||||
<v-btn small text @click="newMask">new mask</v-btn> |
||||
|
||||
</v-col> |
||||
</v-row> |
||||
</v-card-text> |
||||
</v-card> |
||||
</v-col> |
||||
<v-col cols="12" md="12"> |
||||
<v-card width="100%" min-height="100" dark> |
||||
<v-card-text ref="videoPanel"> |
||||
<div id="video_tracks" ref="vidPanelTracks" @mouseleave="allmoveend"> |
||||
<div :ref="'vidtrack'+track.id" v-for="(track, index) in mainTracks" :key="index" |
||||
style="position: relative;padding: 3px"> |
||||
<div @click="selectTrack(track)" @mousedown="startPositionTrack" @mouseup="allmoveend" |
||||
|
||||
:style="`border:1px solid ${selectedTrack?(selectedTrack.id===track.id?'#f00':'#ccc'):'#ccc'};position: absolute;height:23px;top:${(index*23)}px;left:${((track.start/timeLength)*100)}%;width:${((track.end-track.start)/timeLength)*100}%`"> |
||||
<div style="background: #ededed;right: 0px;top:0px;position: absolute;height: 22px;width: 5px;" |
||||
@mousedown="startChangeSizeTrack" @mouseup="allmoveend()"></div> |
||||
<div>{{ track.text }} ({{ (track.end - track.start).toFixed(2) }} sec)</div> |
||||
<div v-for="(keyframe , index) in track.keyframes" :key="index" |
||||
:style="`position:absolute;height:8px;width:8px;background:red;top:6px;margin-left:-4px;left:${(((keyframe.start - track.start)/track.end)*100)}%`"></div> |
||||
</div> |
||||
</div> |
||||
<div style="position: absolute;width: 5px; background: #aa00ff;height: 120px;top:3px;" |
||||
ref="currentTime" @mousedown="moveStartTime=true" @mouseup="moveStartTime=false"> |
||||
</div> |
||||
</div> |
||||
|
||||
</v-card-text> |
||||
<v-card-actions> |
||||
<v-text-field label="Total Secs" v-model="timeLength"></v-text-field> |
||||
<v-text-field label="Current" v-model="currentTime"></v-text-field> |
||||
<v-btn @click="preview">Start</v-btn> |
||||
<v-btn @click="previewStop">Stop</v-btn> |
||||
</v-card-actions> |
||||
</v-card> |
||||
</v-col> |
||||
</v-row> |
||||
<div ref="vm"> |
||||
<v-icon color="primary" :style="'position: absolute;left:' + moveX + 'px;top:'+ moveY + 'px'" |
||||
v-if="create==='text'">mdi-format-color-text |
||||
</v-icon> |
||||
<v-icon color="primary" :style="'position: absolute;left:' + moveX + 'px;top:'+ moveY + 'px'" |
||||
v-if="create==='image'">mdi-image |
||||
</v-icon> |
||||
<v-icon color="primary" :style="'position: absolute;left:' + moveX + 'px;top:'+ moveY + 'px'" |
||||
v-if="create==='rect'">mdi-rectangle-outline |
||||
</v-icon> |
||||
<div :style="`position: absolute;border:1px solid #ff0000;width:${width}px;height:${height}px;left:${moveX}px;top:${moveY}px`" |
||||
ref="rectcreate" v-if="create==='rect' && action==='paint'"></div> |
||||
<div :style="`position: absolute;border:1px solid #ff0000;width:${width}px;height:${height}px;left:${moveX}px;top:${moveY}px`" |
||||
ref="mask" v-if="oncreateMask && action==='paint'"></div> |
||||
</div> |
||||
</v-container> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: "Animator", |
||||
data: () => ({ |
||||
currentKeyTrack: { |
||||
keyframe: 0, |
||||
track: 0 |
||||
}, |
||||
vidTracks: {}, |
||||
action: null, |
||||
item: null, |
||||
create: null, |
||||
index: 1, |
||||
colorPick: '', |
||||
timeLength: 60, |
||||
moveStartTime: false, |
||||
currentTime: 0, |
||||
oncreateMask: false, |
||||
pinterval: 0, |
||||
width: 0, |
||||
colorMenu: false, |
||||
heigth: 0, |
||||
clickOffset: { |
||||
x: 0, |
||||
y: 0 |
||||
}, |
||||
moveX: 0, |
||||
mainTracks: localStorage.getItem('animator')?JSON.parse(localStorage.getItem('animator')):[], |
||||
moveItem: false, |
||||
moveY: 0, |
||||
selectedTrack: null, |
||||
sizeChangeStart: false, |
||||
positionChangeStart: false, |
||||
editingItem: null |
||||
}), |
||||
mounted() { |
||||
this.$refs.vid.style.height = (this.$refs.vid.offsetWidth / (16 / 9)) + "px" |
||||
window.onmousemove = (event) => { |
||||
this.itemMove(event) |
||||
var bodyRect = document.body.getBoundingClientRect() |
||||
if (this.moveItem && this.currentKeyTrack) { |
||||
var elemRect = this.$refs.vid.getBoundingClientRect(), |
||||
offsetY = elemRect.top - bodyRect.top, |
||||
offsetX = elemRect.left - bodyRect.left |
||||
this.mainTracks[this.currentKeyTrack.track].y = (event.clientY - offsetY) - this.clickOffset.y |
||||
this.mainTracks[this.currentKeyTrack.track].x = (event.clientX - offsetX) - this.clickOffset.x |
||||
this.mainTracks[this.currentKeyTrack.track].keyframes[this.currentKeyTrack.keyframe].x = (event.clientX - offsetX) - this.clickOffset.x |
||||
this.mainTracks[this.currentKeyTrack.track].keyframes[this.currentKeyTrack.keyframe].y = (event.clientY - offsetY) - this.clickOffset.y |
||||
this.mainTracks.__ob__.dep.notify() |
||||
} |
||||
if (this.moveStartTime) { |
||||
console.log('') |
||||
|
||||
var vidtrack = this.$refs.vidPanelTracks.getBoundingClientRect() |
||||
var offsetXVid = vidtrack.left - bodyRect.left |
||||
var pos = (event.clientX - offsetXVid + 12) |
||||
if (pos >= 15 && pos <= vidtrack.width + 15) { |
||||
this.$refs.currentTime.style.left = pos + "px" |
||||
console.log(offsetXVid, pos, vidtrack.width) |
||||
this.currentTime = (this.timeLength / vidtrack.width) * (pos - 15) |
||||
} |
||||
} |
||||
if (this.selectedTrack && this.positionChangeStart) { |
||||
var bodyRect3 = document.body.getBoundingClientRect(), |
||||
elemRect3 = this.$refs['vidtrack' + this.selectedTrack.id][0].getBoundingClientRect(), |
||||
offsetX3 = elemRect3.left - bodyRect3.left |
||||
var length2 = this.timeLength / 100 |
||||
|
||||
var totalLength2 = (((((event.clientX - this.clickOffset.x) - offsetX3)) / elemRect3.width) * length2) * 100 |
||||
this.mainTracks.forEach(track => { |
||||
var tn = track.end - track.start |
||||
var originalstart = track.start |
||||
if (track.id === this.selectedTrack.id) { |
||||
track.start = totalLength2 |
||||
track.end = track.start + tn |
||||
track.keyframes.forEach(keyframe => { |
||||
var tnx = track.end - track.start |
||||
keyframe.start = track.start + (originalstart - track.start) |
||||
keyframe.end = track.start + tnx |
||||
}) |
||||
} |
||||
}) |
||||
} |
||||
if (this.selectedTrack && this.sizeChangeStart) { |
||||
var bodyRect2 = document.body.getBoundingClientRect(), |
||||
elemRect2 = this.$refs['vidtrack' + this.selectedTrack.id][0].getBoundingClientRect(), |
||||
offsetX2 = elemRect2.left - bodyRect2.left |
||||
var length = this.timeLength / 100 |
||||
|
||||
var totalLength = ((((event.clientX - offsetX2) + 2) / elemRect2.width) * length) * 100 |
||||
this.mainTracks.forEach(track => { |
||||
if (track.id === this.selectedTrack.id) track.end = totalLength |
||||
}) |
||||
} |
||||
} |
||||
window.onkeyup = (event) => { |
||||
console.log(event.key) |
||||
if (event.target === document.body) { |
||||
if (event.key === 'ArrowDown' && this.selectedTrack) this.mainTracks[this.currentKeyTrack.track].keyframes[this.currentKeyTrack.keyframe].y++ |
||||
if (event.key === 'ArrowUp' && this.selectedTrack) this.mainTracks[this.currentKeyTrack.track].keyframes[this.currentKeyTrack.keyframe].y-- |
||||
if (event.key === 'ArrowLeft' && this.selectedTrack) this.mainTracks[this.currentKeyTrack.track].keyframes[this.currentKeyTrack.keyframe].x-- |
||||
if (event.key === 'ArrowRight' && this.selectedTrack) this.mainTracks[this.currentKeyTrack.track].keyframes[this.currentKeyTrack.keyframe].x++ |
||||
if (event.key === "Escape") { |
||||
this.selectedTrack = null |
||||
this.action = null |
||||
this.create = null |
||||
this.moveItem = false |
||||
this.sizeChangeStart = false |
||||
this.positionChangeStart = false |
||||
this.moveStartTime = false |
||||
this.previewStop() |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
watch: { |
||||
|
||||
editingItem(newval, oldval) { |
||||
console.log(newval) |
||||
console.log('old', oldval) |
||||
// if (oldval !== null) { |
||||
// this.vidTracks.forEach(item => { |
||||
// if (item.id === newval.id) item = newval |
||||
// }) |
||||
// } |
||||
}, |
||||
mainTracks (newval) { |
||||
console.log('maintrack change') |
||||
localStorage.setItem('animator', JSON.stringify(newval)) |
||||
}, |
||||
currentTime(newval) { |
||||
if (newval) { |
||||
var videoPanel = this.$refs.vidPanelTracks.getBoundingClientRect() |
||||
var left = videoPanel.width / this.timeLength |
||||
if (!this.moveStartTime) this.$refs.currentTime.style.left = ((left * newval) + 15) + "px" |
||||
this.vidTracks = [] |
||||
this.mainTracks.forEach((track, tindex) => { |
||||
|
||||
if (track.start <= newval && newval <= track.end) { |
||||
var ttrack = track |
||||
var keyframe = null |
||||
var nextframe = null |
||||
track.keyframes.forEach((kvarey, index) => { |
||||
if (kvarey.start <= newval && newval <= kvarey.end) { |
||||
keyframe = kvarey |
||||
|
||||
if (this.selectedTrack) { |
||||
if (this.selectedTrack.id === track.id) { |
||||
this.currentKeyTrack.keyframe = index |
||||
this.currentKeyTrack.track = tindex |
||||
} |
||||
} |
||||
} |
||||
}) |
||||
|
||||
ttrack.x = keyframe.x |
||||
ttrack.y = keyframe.y |
||||
ttrack.width = keyframe.width |
||||
ttrack.height = keyframe.height |
||||
ttrack.opacity = keyframe.opacity |
||||
|
||||
var starter = null |
||||
track.keyframes.forEach((kvarey, index) => { |
||||
if (keyframe.end <= kvarey.start) { |
||||
if (kvarey.start < starter || starter === null) nextframe = index |
||||
if (starter === null) starter = kvarey.start |
||||
} |
||||
}) |
||||
if (nextframe !== null) { |
||||
console.log(nextframe) |
||||
if (track.type === 'text') { |
||||
ttrack.letterSpacing = keyframe.letterSpacing |
||||
ttrack.lineHeight = keyframe.lineHeight |
||||
if (track.keyframes[nextframe].lineHeight !== ttrack.lineHeight) { |
||||
var difflineHeight = (track.keyframes[nextframe].lineHeight - ttrack.lineHeight) |
||||
difflineHeight = difflineHeight / (keyframe.end - keyframe.start) |
||||
difflineHeight = (newval - keyframe.start) * difflineHeight |
||||
ttrack.lineHeight = parseFloat(ttrack.lineHeight) + parseFloat(difflineHeight) |
||||
} |
||||
if (track.keyframes[nextframe].letterSpacing !== ttrack.letterSpacing) { |
||||
var diffletterSpacing = (track.keyframes[nextframe].letterSpacing - ttrack.letterSpacing) |
||||
diffletterSpacing = diffletterSpacing / (keyframe.end - keyframe.start) |
||||
diffletterSpacing = (newval - keyframe.start) * diffletterSpacing |
||||
ttrack.lineHeight = parseFloat(ttrack.letterSpacing) + parseFloat(diffletterSpacing) |
||||
} |
||||
} |
||||
if (track.keyframes[nextframe].x !== ttrack.x) { |
||||
var diffx = (track.keyframes[nextframe].x - ttrack.x) |
||||
diffx = diffx / (keyframe.end - keyframe.start) |
||||
diffx = (newval - keyframe.start) * diffx |
||||
ttrack.x = parseFloat(ttrack.x) + parseFloat(diffx) |
||||
} |
||||
if (track.keyframes[nextframe].width !== ttrack.width) { |
||||
var diffwidth = (track.keyframes[nextframe].width - ttrack.width) |
||||
diffwidth = diffwidth / (keyframe.end - keyframe.start) |
||||
diffwidth = (newval - keyframe.start) * diffwidth |
||||
ttrack.width = parseFloat(ttrack.width) + parseFloat(diffwidth) |
||||
} |
||||
if (track.keyframes[nextframe].height !== ttrack.height) { |
||||
|
||||
console.log('heig', track.keyframes[nextframe].height, ttrack.height) |
||||
var diffheight = (track.keyframes[nextframe].height - ttrack.height) |
||||
diffheight = diffheight / (keyframe.end - keyframe.start) |
||||
diffheight = (newval - keyframe.start) * diffheight |
||||
ttrack.height = parseFloat(ttrack.height) + parseFloat(diffheight) |
||||
console.log(ttrack.height) |
||||
|
||||
} |
||||
if (track.keyframes[nextframe].opacity !== ttrack.opacity) { |
||||
console.log('opa', track.keyframes[nextframe].opacity, ttrack.opacity) |
||||
var diffopacity = (track.keyframes[nextframe].opacity - ttrack.opacity) |
||||
diffopacity = parseFloat(diffopacity) / parseFloat(keyframe.end - keyframe.start) |
||||
console.log(diffopacity) |
||||
diffopacity = (newval - keyframe.start) * diffopacity |
||||
ttrack.opacity = parseFloat(ttrack.opacity) + parseFloat(diffopacity) |
||||
console.log(ttrack.opacity) |
||||
} |
||||
|
||||
if (track.keyframes[nextframe].y !== ttrack.y) { |
||||
var diffy = (track.keyframes[nextframe].y - ttrack.y) |
||||
diffy = diffy / (keyframe.end - keyframe.start) |
||||
diffy = (newval - keyframe.start) * diffy |
||||
ttrack.y = parseFloat(ttrack.y) + parseFloat(diffy) |
||||
} |
||||
} |
||||
this.vidTracks[track.id] = ttrack |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
}, |
||||
methods: { |
||||
newMask() { |
||||
this.oncreateMask = true |
||||
this.create = 'mask' |
||||
this.action = "create" |
||||
}, |
||||
itemMoveStart(event) { |
||||
console.log(event) |
||||
this.moveItem = true |
||||
this.clickOffset.x = event.offsetX |
||||
this.clickOffset.y = event.offsetY |
||||
}, |
||||
preview() { |
||||
this.pinterval = setInterval(() => { |
||||
this.currentTime += 1 / 60 |
||||
if (this.currentTime >= 60) this.currentTime = 0 |
||||
}, 1000 / 60) |
||||
}, |
||||
previewStop() { |
||||
if (this.pinterval) clearInterval(this.pinterval) |
||||
}, |
||||
newKeyFrame() { |
||||
this.mainTracks.forEach(track => { |
||||
if (track.id === this.selectedTrack.id) { |
||||
var pushFrame = null |
||||
var newFrames = [] |
||||
var endTime = null |
||||
track.keyframes.forEach(keyframe => { |
||||
if (keyframe.start <= this.currentTime && keyframe.end >= this.currentTime) { |
||||
pushFrame = { |
||||
start: keyframe.start, |
||||
x: keyframe.x, |
||||
y: keyframe.y, |
||||
width: keyframe.width, |
||||
opacity: keyframe.opacity, |
||||
height: keyframe.height, |
||||
} |
||||
pushFrame.start = this.currentTime |
||||
keyframe.end = this.currentTime |
||||
} |
||||
if (pushFrame && pushFrame.start < keyframe.start) { |
||||
endTime = keyframe.start |
||||
} |
||||
newFrames.push(keyframe) |
||||
}) |
||||
pushFrame.end = endTime |
||||
if (!pushFrame.end) pushFrame.end = track.end |
||||
newFrames.push(pushFrame) |
||||
track.keyframes = newFrames |
||||
} |
||||
}) |
||||
}, |
||||
selectTrack(track) { |
||||
if (this.selectedTrack === track) { |
||||
this.selectedTrack = null |
||||
return |
||||
} |
||||
this.selectedTrack = track |
||||
console.log(this.selectedTrack) |
||||
this.mainTracks.forEach((track, tindex) => { |
||||
|
||||
if (this.selectedTrack.id === track.id) { |
||||
|
||||
track.keyframes.forEach((kvarey, index) => { |
||||
if (kvarey.start <= this.currentTime && this.currentTime <= kvarey.end) { |
||||
this.currentKeyTrack.keyframe = index |
||||
this.currentKeyTrack.track = tindex |
||||
|
||||
} |
||||
}) |
||||
} |
||||
}) |
||||
}, |
||||
startChangeSizeTrack() { |
||||
this.sizeChangeStart = true |
||||
console.log("start size") |
||||
} |
||||
, startPositionTrack(event) { |
||||
|
||||
if (!this.sizeChangeStart) this.positionChangeStart = true |
||||
console.log("start pos", event) |
||||
this.clickOffset.x = event.offsetX |
||||
}, |
||||
deselectTrack() { |
||||
this.selectedTrack = null |
||||
this.sizeChangeStart = false |
||||
this.positionChangeStart = false |
||||
}, |
||||
allmoveend() { |
||||
this.sizeChangeStart = false |
||||
this.positionChangeStart = false |
||||
}, |
||||
colorBorderClear(track) { |
||||
var refItem = this.$refs['item' + track.id][0] |
||||
var id = "" |
||||
if (this.selectedTrack !== null) { |
||||
id = this.selectedTrack.id |
||||
} |
||||
if (id !== track.id) { |
||||
refItem.style.border = "1px solid transparent" |
||||
} |
||||
|
||||
}, |
||||
colorBorder(track) { |
||||
|
||||
var refItem = this.$refs['item' + track.id][0] |
||||
var id = "" |
||||
if (this.selectedTrack !== null) { |
||||
id = this.selectedTrack.id |
||||
} |
||||
if (id !== track.id) { |
||||
refItem.style.border = "1px solid #ededed" |
||||
} |
||||
|
||||
}, |
||||
itemMoveEnd() { |
||||
this.moveItem = false |
||||
}, |
||||
newTrack() { |
||||
this.tracks.push({}) |
||||
}, |
||||
editItem(item) { |
||||
console.log(item) |
||||
this.editingItem = item |
||||
var ref = this.$refs['item' + item.id][0] |
||||
console.log(ref) |
||||
// this.vidTracks.forEach(item => { |
||||
// var refItem = this.$refs['item' + item.id][0] |
||||
// refItem.style.border = "1px solid transparent" |
||||
// |
||||
// }) |
||||
ref.style.border = "1px solid #000" |
||||
}, |
||||
itemMove(event) { |
||||
|
||||
if (this.action) { |
||||
if (this.action === 'create') { |
||||
this.moveX = event.clientX - 30 |
||||
this.moveY = event.clientY - 30 |
||||
} |
||||
|
||||
if (this.action === 'paint') { |
||||
this.width = event.clientX - this.moveX |
||||
this.height = event.clientY - this.moveY |
||||
|
||||
} |
||||
} |
||||
}, |
||||
handleAction() { |
||||
console.log(this.action) |
||||
if (this.action === "create" || this.action === 'paint') { |
||||
var bodyRect = document.body.getBoundingClientRect(), |
||||
elemRect = this.$refs.vid.getBoundingClientRect(), |
||||
offsetY = elemRect.top - bodyRect.top, |
||||
offsetX = elemRect.left - bodyRect.left |
||||
if ((this.create === 'rect' || this.create === 'mask') && this.action === 'create') { |
||||
this.action = 'paint' |
||||
return |
||||
} |
||||
if (this.create === 'mask') { |
||||
|
||||
|
||||
this.mainTracks[this.currentKeyTrack.track].mask = { |
||||
x: this.moveX - offsetX, |
||||
y: this.moveY - offsetY, |
||||
width: this.width, |
||||
height: this.height, |
||||
} |
||||
this.mainTracks.__ob__.dep.notify() |
||||
this.oncreateMask = false |
||||
return |
||||
} |
||||
console.log(offsetY, offsetX) |
||||
var item = { |
||||
x: this.moveX - offsetX, |
||||
y: this.moveY - offsetY, |
||||
width: 0, |
||||
height: 0, |
||||
type: this.create, |
||||
opacity: 1, |
||||
index: this.index, |
||||
id: new Date().getTime(), |
||||
color: "#000", |
||||
mask: null |
||||
} |
||||
if (this.create === 'image') { |
||||
var upload = document.createElement('input') |
||||
upload.type = 'file' |
||||
upload.click() |
||||
upload.onchange = () => { |
||||
const reader = new FileReader(); |
||||
reader.readAsDataURL(upload.files[0]); |
||||
reader.onloadend = () => { |
||||
item.image_data = reader.result |
||||
var image = new Image(); |
||||
image.src = reader.result |
||||
image.onload = () => { |
||||
console.log(item) |
||||
this.index++ |
||||
|
||||
var maintrack = item |
||||
maintrack.keyframes = [{ |
||||
x: item.x, |
||||
y: item.y, |
||||
width: image.width, |
||||
opacity: 1, |
||||
height: image.height, |
||||
start: this.currentTime, |
||||
end: this.timeLength, |
||||
lineHeight: 'default', |
||||
letterSpacing: 'default' |
||||
}] |
||||
maintrack.start = this.currentTime |
||||
maintrack.end = this.timeLength |
||||
this.mainTracks.push(maintrack) |
||||
this.vidTracks[item.id] = item |
||||
this.action = null |
||||
this.create = null |
||||
} |
||||
|
||||
|
||||
} |
||||
console.log(item) |
||||
|
||||
console.log(upload.files) |
||||
} |
||||
} else { |
||||
|
||||
|
||||
console.log(item) |
||||
this.index++ |
||||
|
||||
var maintrack = item |
||||
maintrack.keyframes = [{ |
||||
x: item.x, |
||||
y: item.y, |
||||
width: 0, |
||||
height: 0, |
||||
opacity: 1, |
||||
lineHeight: 'initial', |
||||
letterSpacing: 'initial', |
||||
start: this.currentTime, |
||||
end: this.timeLength |
||||
}] |
||||
if (this.create === 'rect') { |
||||
maintrack.keyframes[0].width = this.width |
||||
maintrack.keyframes[0].height = this.height |
||||
} |
||||
maintrack.start = this.currentTime |
||||
maintrack.end = this.timeLength |
||||
this.mainTracks.push(maintrack) |
||||
|
||||
if (this.create === 'text') { |
||||
item.text = "Text" |
||||
item.font = "Arial" |
||||
item.size = '24px' |
||||
item.color = '#000' |
||||
item.align = 'left' |
||||
item.lineHeight = 'initial' |
||||
item.letterSpacing = 'initial' |
||||
|
||||
} |
||||
|
||||
this.vidTracks[item.id] = item |
||||
this.action = null |
||||
this.create = null |
||||
} |
||||
this.currentTime += 0.01 |
||||
this.selectTrack(maintrack) |
||||
|
||||
} |
||||
}, |
||||
addItemStart(type) { |
||||
console.log(type) |
||||
this.create = type |
||||
this.action = 'create' |
||||
}, |
||||
clearStart() { |
||||
this.create = null |
||||
}, |
||||
getTracks() { |
||||
this.tracks.forEach(item => { |
||||
console.log(item) |
||||
}) |
||||
} |
||||
|
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
#video_edit { |
||||
width: 1280px; |
||||
height: 720px; |
||||
border: 1px solid #ccc; |
||||
} |
||||
|
||||
#video_tracks { |
||||
min-height: 100px; |
||||
padding: 2px; |
||||
width: 100%; |
||||
border: 1px solid #ccc; |
||||
overflow-y: auto; |
||||
} |
||||
</style> |
@ -0,0 +1,15 @@ |
||||
<template> |
||||
<obs-vue server="ws://localhost:4444/"></obs-vue> |
||||
</template> |
||||
|
||||
<script> |
||||
import ObsVue from "@/lib/ObsVue"; |
||||
export default { |
||||
name: "StreamControl", |
||||
components: {ObsVue} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
</style> |
@ -0,0 +1,52 @@ |
||||
<template> |
||||
<div style="width: 1280px; height: 720px"> |
||||
|
||||
<RemoteFeedOnly @kick="kick" @rtp="rtp" v-if="rid" :creator="creator" :user="$route.params.user" :room="rid" server="ws://localhost:8188/ws" :username="name"></RemoteFeedOnly> |
||||
<!-- <ObsVue server="ws://localhost:4444/"></ObsVue>--> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
// import VueJanus from "@/lib/VueJanus"; |
||||
import RemoteFeedOnly from "@/lib/RemoteFeedOnly"; |
||||
export default { |
||||
name: "RoomView", |
||||
// components: {VueJanus}, |
||||
components: {RemoteFeedOnly}, |
||||
data: () => ({ |
||||
rid:'', |
||||
name:'', |
||||
dialog:false, |
||||
creator:false |
||||
}), |
||||
mounted () { |
||||
// if (!name) this.dialog = true |
||||
this.getRoom() |
||||
}, |
||||
methods: { |
||||
kick (user) { |
||||
this.$axios.get(`kick/${this.$route.params.id}/${user}`).then(response => { |
||||
console.log(response) |
||||
}) |
||||
}, |
||||
rtp (user) { |
||||
this.$axios.get(`rtp/${this.$route.params.id}/${user}`).then(response => { |
||||
console.log(response) |
||||
}) |
||||
}, |
||||
getRoom () { |
||||
this.dialog = false |
||||
this.$axios.get(`room/${this.$route.params.id}`).then(response => { |
||||
this.rid = response.data.rid |
||||
this.creator = response.data.can_modify |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
body , html, div { |
||||
overflow: hidden; |
||||
} |
||||
</style> |
Loading…
Reference in new issue