sort out order of operations for vue components

This commit is contained in:
Matthew Gaffen 2019-08-16 18:18:59 +01:00
parent 147e31d03c
commit 480e20a722
6 changed files with 700 additions and 569 deletions

View File

@ -8,6 +8,9 @@
<h2>Todo List</h2>
{% vue { component:"ListComp"} %}
{% vue { component:"ListComp", props: { startlist: [{message:"Render Vue in Nunjucks"}, {message:"???"}, {message:"Profit!"}] } } %}
{% vue { component:"RecordPlayer", props: { record: "https://hearsepileup.rip/music/last_time.mp3" } } %}
{% vue { component:"RecordPlayer", props: { record: "https://hearsepileup.rip/music/generation_y.mp3" } } %}
{% vue { component:"RecordPlayer", props: { record: "https://hearsepileup.rip/music/pretty_shiny_things.mp3" } } %}
<section id="content" role="main" class="content-wrapper">
{{content|safe}}
</section>

776
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@
"devDependencies": {
"@11ty/eleventy": "^0.8.3",
"@babel/core": "^7.5.5",
"@babel/node": "^7.5.0",
"@babel/node": "^7.5.5",
"@babel/plugin-transform-modules-commonjs": "^7.5.0",
"@babel/preset-env": "^7.5.5",
"autoprefixer": "^9.6.1",
@ -25,24 +25,24 @@
"clean-webpack-plugin": "^3.0.0",
"co": "^4.6.0",
"compression": "^1.7.4",
"css-loader": "^3.0.0",
"css-loader": "^3.2.0",
"debug": "^4.1.1",
"file-loader": "^4.0.0",
"file-loader": "^4.2.0",
"lodash.debounce": "^4.0.8",
"mini-css-extract-plugin": "^0.7.0",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.12.0",
"optimize-js-plugin": "0.0.4",
"postcss-loader": "^3.0.0",
"require-from-string": "^2.0.2",
"sass-loader": "^7.1.0",
"sass-loader": "^7.2.0",
"strip-ansi": "^5.2.0",
"style-loader": "^0.23.1",
"style-loader": "^1.0.0",
"tree-kill": "^1.2.1",
"url-loader": "^2.0.1",
"url-loader": "^2.1.0",
"vue-loader": "^15.7.1",
"vue-server-renderer": "^2.6.10",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.35.3",
"webpack": "^4.39.2",
"webpack-assets-manifest": "^3.1.1",
"webpack-cli": "^3.3.6"
},

View File

@ -158,10 +158,10 @@ const build_site = debounce(
200
);
build_site();
const compiler = webpack(config(process.env.NODE_ENV));
compiler.watch({}, err => {
sync.reload();
});
build_site();

View File

@ -0,0 +1,461 @@
<template lang="html">
<div class="RecordPlayer" v-bind:data-record="recordUrl">
<button class="toggle" v-on:click="togglePlay">
<svg v-if="playing === false">
<use :xlink:href="playIcon" />
</svg>
<svg v-else>
<use :xlink:href="pauseIcon" />
</svg>
</button>
<canvas
class="disc"
ref="disc"
v-bind:width="canvasSize"
v-bind:height="canvasSize"
v-on:mousedown="beginScrubbing"
/>
<!-- <p class="devinfo">Scrubbing: {{ scrubbing }}</p> -->
</div>
</template>
<script>
export default {
props: ["record"],
data: function(args) {
let maxRecordSize = 320 - 40;
let maxRecordInnerSize = 100;
return {
playing: false,
scrubbing: false,
playIcon: "#playbutton",
pauseIcon: "#pausebutton",
playPosition: 0,
buffered: 0,
mousePos: null,
initialised: false,
bassBoom: 1,
canvasSize: 320,
maxRecordSize,
minRecordSize: maxRecordSize - maxRecordSize / 6,
maxRecordInnerSize,
recordInnerSize: maxRecordInnerSize,
recordSize: maxRecordSize,
eqBarCache: null,
eqBarWidth: 0,
eqBarRotation: 0,
eqSampleSize: 2,
recordUrl: this.record
};
},
mounted: function() {
this.audioElement = document.createElement("audio");
this.audioElement.crossOrigin = 'anonymous';
console.log(this.record);
this.audioElement.src = this.record;
let disc = this.$refs.disc;
this.eqBarCache = document.createElement("canvas");
this.eqBarCacheWhite = document.createElement("canvas");
// this.eqBarCache = this.$refs.cache;
this.eqBarCache.width = 80;
this.eqBarCache.height = disc.height / 2;
this.eqBarCacheWhite.width = 80;
this.eqBarCacheWhite.height = disc.height / 2;
this.discCtx = disc.getContext("2d");
this.drawUI();
this.audioElement.addEventListener("playing", () => {
if (!this.initialised) {
this.initialised = true;
}
if (!this.analyser) {
this.audioCtx = new (window.AudioContext ||
window.webkitAudioContext)();
this.analyser = this.audioCtx.createAnalyser();
this.source = this.audioCtx.createMediaStreamSource(
this.audioElement.captureStream()
);
this.source.connect(this.analyser);
this.analyser.fftSize = 256;
this.analyser.smoothingTimeConstant = 0.3;
this.bufferLength = this.analyser.frequencyBinCount;
this.dataArray = new Uint8Array(this.bufferLength);
this.freqArray = new Uint8Array(this.bufferLength);
let segmentWidth =
360 / (this.analyser.fftSize / (this.eqSampleSize * 2));
this.eqBarWidth = (segmentWidth / 5) * 3 * (Math.PI / 180);
this.eqBarRotation = segmentWidth * (Math.PI / 180);
this.createBarData();
} else {
// let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
this.source = this.audioCtx.createMediaStreamSource(
this.audioElement.captureStream()
);
this.source.connect(this.analyser);
}
});
this.audioElement.addEventListener("ended", () => {
this.playing = false;
this.initialised = false;
this.drawUI();
});
},
methods: {
createBarData: function() {
this.drawBar(this.eqBarCache, "rgb(0, 0, 0)");
this.drawBar(this.eqBarCacheWhite, "rgb(255, 255, 255)");
},
drawBar: function(canvas, colour) {
let ctx = canvas.getContext("2d");
let disc = this.$refs.disc;
ctx.fillStyle = "rgba(0, 0, 0, 0.0)";
ctx.clearRect(0, 0, ctx.width, ctx.height);
ctx.fillStyle = colour;
ctx.beginPath();
ctx.moveTo(0, this.eqBarCache.height);
ctx.arc(
0,
disc.height / 2,
disc.height / 2,
0 - Math.PI / 2,
this.eqBarWidth - Math.PI / 2
// Math.PI * 2
);
// ctx.moveTo(0, this.eqBarCache.height);
ctx.closePath();
ctx.fill();
},
draw: function() {
if (this.playing || this.scrubbing) {
let drawVisual = requestAnimationFrame(this.draw);
}
let disc = this.$refs.disc;
let ctx = this.discCtx;
ctx.clearRect(0, 0, disc.width, disc.height);
this.drawUI();
if (this.analyser) {
this.runAnalysis();
}
},
drawUI: function() {
let ctx = this.discCtx;
let disc = this.$refs.disc;
// Outer ring
ctx.lineWidth = 3;
ctx.beginPath();
ctx.strokeStyle = "rgb(38, 38, 38)";
ctx.arc(
disc.width / 2,
disc.height / 2,
disc.width / 2 - 4,
0,
Math.PI * 2
);
ctx.closePath();
ctx.stroke();
// Inner Ring
ctx.beginPath();
ctx.arc(disc.width / 2, disc.height / 2, 80 / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.stroke();
if (this.initialised) {
this.drawRecord();
}
},
runAnalysis: function() {
let ctx = this.discCtx;
let disc = this.$refs.disc;
this.analyser.getByteFrequencyData(this.dataArray);
this.analyser.getByteTimeDomainData(this.freqArray);
let bassSample = this.dataArray.slice(this.dataArray.length / 4);
let bassVolume =
bassSample.reduce((accumulated, vol) => {
return accumulated + vol;
}, 0) /
(this.dataArray.length / 4);
this.bassBoom = bassVolume / 255;
let variance = this.bassBoom * (this.maxRecordSize - this.minRecordSize);
this.recordSize = this.minRecordSize + variance;
this.recordInnerSize = this.maxRecordInnerSize + variance;
},
drawRecord: function() {
let ctx = this.discCtx;
let disc = this.$refs.disc;
// White Disc
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.beginPath();
ctx.arc(
disc.width / 2,
disc.height / 2,
this.recordSize / 2,
0,
Math.PI * 2,
false
);
ctx.arc(
disc.width / 2,
disc.height / 2,
this.recordInnerSize / 2,
0,
Math.PI * 2,
true
);
ctx.closePath();
ctx.fill();
if (this.audioElement.buffered.length > 0) {
let buffered = this.audioElement.buffered;
this.buffered =
(this.audioElement.buffered.end(
this.audioElement.buffered.length - 1
) /
this.audioElement.duration) *
100;
}
if (this.audioElement.duration) {
this.playPosition =
(this.audioElement.currentTime / this.audioElement.duration) * 100;
}
// Buffered Ring
this.drawProgressBar("rgb(200, 200, 200)", this.buffered);
// Playhead Position
this.drawProgressBar("rgb(0, 0, 0)", this.playPosition);
this.drawEqualizerBars();
},
drawProgressBar: function(color, percentage) {
let ctx = this.discCtx;
let disc = this.$refs.disc;
let startPos = Math.PI * 2 - Math.PI / 2;
let progressPosition = startPos + ((Math.PI * 2) / 100) * percentage;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(
disc.width / 2,
disc.height / 2,
this.recordSize / 2,
progressPosition,
startPos,
true
);
ctx.lineTo(disc.width / 2, disc.height / 2 - 11 / 2);
ctx.arc(
disc.width / 2,
disc.height / 2,
this.recordInnerSize / 2,
startPos,
progressPosition,
false
);
ctx.closePath();
ctx.fill();
},
drawEqualizerBars() {
let ctx = this.discCtx;
let disc = this.$refs.disc;
let dataPoints = [];
let rotateAmount = this.eqBarRotation;
// console.log(this.freqArray.length / this.eqSampleSize);
for (let i = 0; i < this.freqArray.length; i += this.eqSampleSize) {
dataPoints.push(this.freqArray.slice(i, i + this.eqSampleSize));
}
dataPoints.forEach((freqData, i) => {
let mean =
freqData.reduce((dataPoint, total) => {
return total + dataPoint;
}, 0) / freqData.length;
let multiplier = (mean - 126) / 100;
let ringWidth = this.recordSize - this.recordInnerSize;
let barSize = (this.recordInnerSize + ringWidth * multiplier) / 2;
barSize = barSize >= 0 ? barSize : 0;
// if (i === 0) {
// console.log(barSize, this.recordInnerSize);
// }
ctx.save();
ctx.beginPath();
// if (barSize < this.recordInnerSize / 2) {
ctx.arc(
disc.width / 2,
disc.height / 2,
this.recordInnerSize / 2,
0,
Math.PI * 2,
barSize <= this.recordInnerSize / 2
);
// }
// if (i === 3) {
// console.log(
// this.playPosition,
// i / dataPoints.length * 100,
// this.playPosition < i / dataPoints.length * 100
// );
// }
ctx.arc(
disc.width / 2,
disc.height / 2,
barSize,
0,
Math.PI * 2,
barSize > this.recordInnerSize / 2
);
ctx.closePath();
ctx.translate(disc.width / 2, disc.height / 2);
ctx.rotate(rotateAmount * i);
ctx.clip();
ctx.drawImage(
barSize > this.recordInnerSize / 2 &&
this.playPosition < (i / dataPoints.length) * 100
? this.eqBarCache
: this.eqBarCacheWhite,
/*disc.width / 2*/ 0,
-(disc.height / 2)
);
ctx.rotate(-(rotateAmount * i));
ctx.translate(-(disc.width / 2), -(disc.height / 2));
ctx.restore();
});
},
togglePlay: function() {
if (!this.playing) {
this.audioElement.play();
this.playing = !this.playing;
this.draw();
} else {
this.audioElement.pause();
this.playing = !this.playing;
}
},
beginScrubbing: function(e) {
if (this.initialised) {
this.scrubbing = true;
this.draw();
window.addEventListener("mouseup", this.finishScrubbing);
window.addEventListener("mousemove", this.scrub);
this.scrub(e);
if (e.stopPropagation) e.stopPropagation();
if (e.preventDefault) e.preventDefault();
e.cancelBubble = true;
e.returnValue = false;
return false;
}
},
scrub: function(e) {
let discPos = this.$refs.disc.getBoundingClientRect();
let centre = {
x: discPos.x + discPos.width / 2,
y: discPos.y + discPos.height / 2
};
let mousePos = { x: e.clientX, y: e.clientY };
let angle =
Math.atan2(centre.y - mousePos.y, centre.x - mousePos.x) +
Math.PI +
Math.PI / 2;
angle = angle > Math.PI * 2 ? angle - Math.PI * 2 : angle;
let position = angle / (Math.PI * 2);
this.audioElement.currentTime = this.audioElement.duration * position;
},
finishScrubbing: function(e) {
this.scrubbing = false;
window.removeEventListener("mouseup", this.finishScrubbing);
window.removeEventListener("mousemove", this.scrub);
}
}
};
</script>
<style lang="scss" scoped>
// @import "~styles/_vars.scss";
.RecordPlayer {
position: relative;
width: 160px;
height: 160px;
}
.disc {
width: 160px;
height: 160px;
&:hover {
cursor: pointer;
}
}
.toggle {
position: absolute;
width: 38px;
height: 38px;
top: 61px;
left: 61px;
border: 0;
background: none;
border-radius: 50%;
background: linear-gradient(#191919, #000);
&:focus {
outline: none;
}
&:hover {
cursor: pointer;
background: linear-gradient(#000, #191919);
}
svg {
fill: white;
width: 10px;
height: 10px;
display: block;
margin: auto;
}
}
.devinfo {
position: absolute;
top: 100%;
width: 100%;
display: block;
text-align: center;
}
</style>

View File

@ -1,6 +1,7 @@
import Vue from "vue";
import HelloWorld from "./components/HelloWorld.vue";
import ListComp from "./components/ListComp.vue";
import RecordPlayer from "./components/RecordPlayer.vue";
Array.prototype.forEach.call(document.querySelectorAll(".HelloWorld"), function(
elem
@ -17,3 +18,11 @@ Array.prototype.forEach.call(document.querySelectorAll(".ListComp"), function(
render: h => h(ListComp, { props: { startlist: elem.dataset.startlist } })
}).$mount(elem);
});
Array.prototype.forEach.call(document.querySelectorAll(".RecordPlayer"), function(
elem
) {
new Vue({
render: h => h(RecordPlayer, { props: { record: elem.dataset.record } })
}).$mount(elem);
});