Trying to learn Vue.js... This is a remix of one of my previous pens that we used at work for an internal hackathon. Converted this to Vue.js from AngularJS. Boo! Firefox doesn't like CSS writing-mode yet.
A Pen by adammmanka on CodePen.
<main> | |
<section v-for="(day, index) in schedule"> | |
<header> | |
Day {{index+1}} — {{day.date | date}} | |
</header> | |
<ul> | |
<li v-for="slot in day.agenda" v-bind:class="{ current: checkTime(slot.range[0], slot.range[1]) }"> | |
<h3><b>{{slot.display.h}}</b>{{slot.display.m}}{{slot.display.a}}</h3> | |
<div v-html="slot.desc"></div> | |
<div><small v-html="slot.location"></small></div> | |
</li> | |
<li class="after-hours"> | |
<div> | |
<b>Day {{index+1}}</b> | |
<br />Before / After Hours | |
<br /><small><small>Click the Event Scroller</small></small> | |
</div> | |
</li> | |
</ul> | |
</section> | |
</main> | |
<aside> | |
<div class="clock"> | |
<h3>{{time}}</h3> | |
</div> | |
<div class="control" v-bind:class="{ show: showTimeTraveller }"> | |
<header v-on:click="showTimeTraveller = !showTimeTraveller"> | |
<div><b>Event Scroller</b></div> | |
<button><span>_</span></button> | |
</header> | |
<div> | |
<p><input type="range" min="0" max="23.9" step="0.1" v-model="now" id="traveler" /></p> | |
<p>To-do: Multi-day Traversing</p> | |
</div> | |
</div> | |
<h1>Coinvention</h1> | |
<svg class="logo" viewBox="0 0 300 300"> | |
<path d=""></path> | |
<path d="M177.832 138.417 C 162.089 155.760,169.908 184.435,192.333 191.598 C 220.200 200.501,244.954 171.302,231.731 145.126 C 230.159 142.014,225.344 135.864,224.868 136.360 C 224.457 136.787,220.000 144.673,220.000 144.974 C 220.000 145.171,220.521 146.057,221.158 146.944 C 229.404 158.421,225.273 174.618,212.513 180.845 C 191.460 191.117,170.367 166.950,183.391 147.478 C 184.305 146.111,185.004 144.878,184.943 144.739 C 184.482 143.671,180.119 136.338,179.945 136.336 C 179.823 136.335,178.872 137.271,177.832 138.417 M219.344 264.250 L 219.333 277.000 220.828 277.000 L 222.323 277.000 222.411 268.046 L 222.500 259.093 231.654 268.713 C 236.688 274.004,240.851 278.333,240.904 278.333 C 240.957 278.333,241.000 272.558,241.000 265.500 L 241.000 252.667 239.500 252.667 L 238.000 252.667 237.996 261.750 L 237.991 270.833 234.903 267.667 C 233.205 265.925,229.012 261.575,225.585 258.000 L 219.355 251.500 219.344 264.250 M322.000 264.333 L 322.000 277.000 323.495 277.000 L 324.989 277.000 325.078 267.992 L 325.167 258.985 334.288 268.576 C 339.305 273.851,343.542 278.217,343.705 278.278 C 343.873 278.341,344.000 272.859,344.000 265.528 L 344.000 252.667 342.339 252.667 L 340.677 252.667 340.589 261.679 L 340.500 270.692 331.500 261.208 C 326.550 255.992,322.387 251.711,322.250 251.695 C 322.112 251.680,322.000 257.367,322.000 264.333 M66.000 252.437 C 53.266 255.346,52.827 273.841,65.412 277.192 C 67.774 277.820,71.216 277.787,73.295 277.116 L 75.000 276.565 75.000 273.140 L 75.000 269.715 73.103 270.858 C 68.374 273.705,62.785 271.013,62.401 265.704 C 61.905 258.847,69.321 255.118,74.250 259.745 L 75.000 260.450 75.000 256.923 L 75.000 253.396 73.454 252.869 C 71.276 252.127,68.150 251.946,66.000 252.437 M93.927 252.324 C 81.719 254.530,79.781 272.189,91.251 276.708 C 100.075 280.183,109.551 274.686,109.955 265.858 C 110.375 256.687,103.220 250.645,93.927 252.324 M295.095 252.799 C 281.361 257.271,284.693 277.455,299.167 277.455 C 315.817 277.455,316.308 252.926,299.667 252.435 C 297.643 252.375,295.993 252.506,295.095 252.799 M119.667 264.833 L 119.667 277.000 122.500 277.000 L 125.333 277.000 125.333 264.833 L 125.333 252.667 122.500 252.667 L 119.667 252.667 119.667 264.833 M136.000 264.833 L 136.000 277.000 138.827 277.000 L 141.654 277.000 141.744 269.457 L 141.833 261.914 148.142 269.457 L 154.451 277.000 157.059 277.000 L 159.667 277.000 159.667 264.833 L 159.667 252.667 156.840 252.667 L 154.013 252.667 153.923 260.237 L 153.833 267.807 147.500 260.246 L 141.167 252.685 138.583 252.676 L 136.000 252.667 136.000 264.833 M167.976 253.417 C 168.137 253.829,170.249 259.342,172.669 265.667 C 175.089 271.992,177.208 277.525,177.377 277.963 C 177.547 278.401,177.757 278.688,177.844 278.600 C 178.049 278.396,187.665 253.249,187.666 252.917 C 187.667 252.477,184.416 252.635,184.250 253.083 C 184.165 253.313,182.700 257.294,180.994 261.930 C 179.289 266.567,177.790 270.242,177.663 270.097 C 177.536 269.952,176.022 265.972,174.299 261.252 L 171.167 252.670 169.425 252.668 C 167.754 252.667,167.695 252.697,167.976 253.417 M196.333 264.833 L 196.333 277.000 202.667 277.000 L 209.000 277.000 209.000 275.667 L 209.000 274.333 204.167 274.333 L 199.333 274.333 199.333 269.833 L 199.333 265.333 204.167 265.333 L 209.000 265.333 209.000 263.833 L 209.000 262.333 204.167 262.333 L 199.333 262.333 199.333 258.833 L 199.333 255.333 204.167 255.333 L 209.000 255.333 209.000 254.000 L 209.000 252.667 202.667 252.667 L 196.333 252.667 196.333 264.833 M249.667 254.000 L 249.667 255.333 252.667 255.333 L 255.667 255.333 255.667 266.167 L 255.667 277.000 257.167 277.000 L 258.667 277.000 258.667 266.167 L 258.667 255.333 261.667 255.333 L 264.667 255.333 264.667 254.000 L 264.667 252.667 257.167 252.667 L 249.667 252.667 249.667 254.000 M273.333 264.833 L 273.333 277.000 274.833 277.000 L 276.333 277.000 276.333 264.833 L 276.333 252.667 274.833 252.667 L 273.333 252.667 273.333 264.833 M302.916 256.001 C 310.287 259.257,310.697 269.590,303.611 273.515 C 299.685 275.690,294.425 274.521,291.627 270.852 C 285.599 262.949,293.906 252.022,302.916 256.001 M99.836 258.288 C 102.658 259.570,104.178 262.320,103.933 265.704 C 103.314 274.263,90.353 274.263,89.734 265.704 C 89.316 259.925,94.715 255.962,99.836 258.288 " transform="translate(159.603449, 185.823334) rotate(180.000000) translate(-159.603449, -185.823334)"></path> | |
</svg> | |
</aside> |
Trying to learn Vue.js... This is a remix of one of my previous pens that we used at work for an internal hackathon. Converted this to Vue.js from AngularJS. Boo! Firefox doesn't like CSS writing-mode yet.
A Pen by adammmanka on CodePen.
((window) => { | |
let now = moment() | |
// JSON DATA | |
let schedule = [ | |
{ | |
date: now, | |
agenda: [ | |
{range: ['08:00', '08:59'], display: {h:8, m:'', a: 'am'}, location: '', desc: 'Morning Reception'}, | |
{range: ['09:00', '09:10'], display: {h:9, m:'', a: 'am'}, location: '', desc: 'Opening Remarks - The Future Will Be In Blockchain'}, | |
{range: ['09:15', '09:34'], display: {h:9, m:15, a: 'am'}, location: '', desc: ''}, | |
{range: ['09:35', '09:54'], display: {h:9, m:35, a: 'am'}, location: 'Thomas Jay Rush (*MORE TBA*)', desc: 'Fireside Chat: The Value Of Blockchains'}, | |
{range: ['09:55', '10:10'], display: {h:9, m:55, a: 'am'}, location: 'Bryan Dube', desc: 'Building the Infrastructure of Change: Educating and educating the future generation'}, | |
{range: ['10:10', '10:39'], display: {h:10, m:10, a: 'am'}, location: '', desc: 'Community Engagements Within Blockchain Technology'}, | |
{range: ['10:40', '10:59'], display: {h:10, m:40, a: 'am'}, location: 'Simon Manka', desc: 'Careers in Cryptocurrency'}, | |
{range: ['11:00', '11:25'], display: {h:11, m:'', a: 'am'}, location: '', desc: 'Cryptoeconomics - Blockchain governance mechanisms, consensus mechanisms, chain interoperability'}, | |
{range: ['11:30', '11:59'], display: {h:11, m:30, a: 'am'}, location: 'Kevin Owocki', desc: 'Developers Developers Developers: The Job Market in a Blockchain World'}, | |
{range: ['12:00', '12:14'], display: {h:'Noon', m:'', a: '-ish'}, location: '', desc: 'TBA'}, | |
{range: ['12:15', '12:44'], display: {h:12, m:15, a: 'pm'}, location: '', desc: 'Legal - Regulations, Utility vs Equity, ICO vs STO, Decentralized business models and the law'}, | |
{range: ['12:45', '13:39'], display: {h:12, m:45, a: 'pm'}, location: '', desc: 'LUNCH'}, | |
{range: ['13:40', '14:24'], display: {h:1, m:40, a: 'pm'}, location: '', desc: 'ICO/STO Presentation Period'}, | |
{range: ['14:25', '14:45'], display: {h:2, m:25, a: 'pm'}, location: '', desc: 'The Philadelphia Blockchain Community: <br> Discussing Philadelphia’s Blockchain Strengths, Weaknesses, and Future'}, | |
{range: ['14:55', '15:14'], display: {h:2, m:55, a: 'pm'}, location: 'Alexandra Levin Kramer Esq.', desc: ''}, | |
{range: ['15:15', '15:35'], display: {h:3, m:15, a: 'pm'}, location: '', desc: ''}, | |
{range: ['15:40', '15:54'], display: {h:3, m:40, a: 'pm'}, location: 'Ready set crypto, Crypto Love, (*More TBA*)', desc: 'Youtube influencer panel: Their picks, their thoughts and their vision of the Future in Blockchain'}, | |
{range: ['15:55', '16:19'], display: {h:3, m:55, a: 'pm'}, location: '', desc: 'The Great Healthcare & Data Exchange: The implications and social impact of bringing blockchain technology to forefront of healthcare markets.'}, | |
{range: ['16:20', '16:35'], display: {h:4, m:20, a: 'pm'}, location: '', desc: 'TBA'}, | |
{range: ['16:40', '17:59'], display: {h:4, m:40, a: 'pm'}, location: 'Samson Williams', desc: 'Humans - The only customers you’ve got til AIs take over'}, | |
{range: ['17:00', '17:24'], display: {h:5, m:10, a: 'pm'}, location: '', desc: 'TBA'}, | |
{range: ['17:25', '17:40'], display: {h:5, m:25, a: 'pm'}, location: 'Philip Forte and Tyler Wellner', desc: 'Block Venture Coalition'}, | |
{range: ['17:45', '17:59'], display: {h:5, m:45, a: 'pm'}, location: '', desc: 'TBA'}, | |
{range: ['18:00', '18:29'], display: {h:6, m:'', a: 'pm'}, location: '', desc: 'Self Sovereign Identity & Data Privacy- The protection of our data is growing ever more important with the rise of decentralized systems.<br> These experts are working on not letting data become compromised in an open network.'}, | |
{range: ['18:30', '18:49'], display: {h:6, m:30, a: 'pm'}, location: '', desc: 'TBA'}, | |
{range: ['18:50', '19:00'], display: {h:6, m:50, a: 'pm'}, location: '', desc: 'Closing Remarks'} | |
] | |
// }, | |
// { | |
// date: moment(now).add(1, 'day'), | |
// agenda: [ | |
// {start: 0, end: 859, display: {h:8, m:30, a: 'am'}, location: 'Underscore Coffee Bar', desc: 'Breakfast Club Donuts + Coffee'}, | |
// {start: 900, end: 959, display: {h:9, m:'', a: 'am'}, location: 'Solaris Terminal', desc: 'Aspirin Session Open forum to discuss headaches and pains, Q&A, tools & tricks, etc.'}, | |
// {start: 1000, end: 1159, display: {h:10, m:'', a: 'am'}, location: '', desc: 'Hacking Session 3'}, | |
// {start: 1200, end: 1229, display: {h:'Noon', m:'', a: '-ish'}, location: 'Break Room', desc: 'LUNCH: Taco Tuesday!'}, | |
// {start: 1230, end: 1529, display: {h:12, m:30, a: 'pm'}, location: '', desc: 'Hacking Session 4'}, | |
// {start: 1530, end: 1900, display: {h:3, m:30, a: 'pm'}, location: 'Underscore Coffee Bar', desc: 'Team Check-in'}, | |
// {start: 1901, end: 1959, display: {h:7, m:'', a: 'pm'}, location: 'Hackers Bar & Grill <br /><small>@ Second and Main</small>', desc: 'Dinner!'} | |
// ] | |
// }, | |
// { | |
// date: moment(now).add(2, 'day'), | |
// agenda: [ | |
// {start: 0, end: 829, display: {h:8, m:30, a: 'am'}, location: 'Underscore Coffee Bar', desc: 'Breakfast Club Donuts + Coffee'}, | |
// {start: 1000, end: 1159, display: {h:10, m:'', a: 'am'}, location: '', desc: 'Hacking Session 5'}, | |
// {start: 1200, end: 1229, display: {h:'Noon', m:'', a: '-ish'}, location: 'Break Room', desc: 'LUNCH: Taco Tuesday!'}, | |
// {start: 1230, end: 1529, display: {h:12, m:30, a: 'pm'}, location: '', desc: 'am Hacking Session 6'}, | |
// {start: 1530, end: 1900, display: {h:3, m:30, a: 'pm'}, location: 'Underscore Coffee Bar', desc: 'Show & Tell Happy Hour'}, | |
// ] | |
} | |
] | |
let timeFromNum = (num, sep, ampm) => { | |
let hh = parseInt(num) | |
let mm = Math.round((num-hh) * 60) | |
sep = sep || '' | |
return (hh>12&&m?hh-12:hh)+sep+('00'+mm).substr(-2)+(ampm?(hh>11?' pm':' am'):'') | |
} | |
let numFromTime = (time) => { | |
let set = time.split(/[.:]/) | |
let hh = parseInt(set[0]) | |
let mm = set[1] ? parseInt(set[1]) : 0 | |
return parseFloat((hh + mm / 60).toFixed(1)) | |
} | |
let app = new Vue({ | |
el: 'aside', | |
data: { | |
now: numFromTime(moment(now).format('HH:mm')), | |
time: moment().format('h:mm a'), | |
showTimeTraveller: false | |
} | |
}) | |
let sked = new Vue({ | |
el: 'main', | |
filters: { | |
date: function(date) { | |
return date.format('ddd, MMM D'); | |
} | |
}, | |
data: { | |
now: numFromTime(moment(now).format('HH:mm')), | |
schedule: schedule | |
}, | |
methods: { | |
checkTime: function(ts, te) { | |
return (this.now >= numFromTime(ts) && this.now < numFromTime(te)) | |
} | |
} | |
}) | |
let setClockPos = () => { | |
setTimeout(() => { | |
let anchor = document.querySelector('.current') | |
let t = '1em' | |
if(anchor) { | |
t = Math.round(anchor.getBoundingClientRect().top) + 'px' | |
} | |
document.documentElement.style.setProperty('--y', t) | |
}, 350) | |
} | |
let timeTraveler | |
let setTime = function() { | |
let now = moment() | |
app.now = sked.now = numFromTime(moment(now).format('HH:mm')) | |
app.time = moment(now).format('h:mm a') | |
} | |
let runTimer = () => { | |
setClockPos() | |
timeTraveler = setInterval(function() { | |
setTime() | |
}, 30000) | |
} | |
runTimer() | |
document.querySelector('#traveler').addEventListener('input', (e) => { | |
app.time = timeFromNum(e.target.value, ':', true) | |
sked.now = e.target.value | |
setClockPos() | |
clearInterval(timeTraveler) | |
}, false) | |
document.querySelector('.control header').addEventListener('click', (e) => { | |
setTime() | |
runTimer() | |
}, false) | |
let randum = function(min, max) { | |
return Math.round((Math.random() * min) + (Math.random() * max)); | |
} | |
let randex = function() { | |
return '#' + ( '00' + Math.floor( Math.random() * 16777216 ).toString(16) ).substr(-6) | |
} | |
let colorizer = () => { | |
let hex = randex() | |
let reverseHex = '#' + hex.replace('#', '').split("").reverse().join("") | |
document.documentElement.style.setProperty('--bg', hex) | |
document.documentElement.style.setProperty('--accent', reverseHex) | |
} | |
let transformer = () => { | |
document.documentElement.style.setProperty('--transform', 'translate(-50%, -50%) rotate(' + randum(-360, 360) + 'deg)'); | |
} | |
setTimeout(() => { | |
colorizer() | |
}, 1000) | |
setTimeout(() => { | |
transformer() | |
}, 100) | |
let adventureTime = window.setInterval(function() { | |
colorizer() | |
}, 7500); | |
let partyTime = window.setInterval(function() { | |
transformer() | |
}, 12000); | |
// resize capture | |
let resizeTimer | |
window.addEventListener('resize', (e) => { | |
clearTimeout(resizeTimer); | |
resizeTimer = setTimeout(() => { | |
setClockPos() | |
}, 60); | |
}, false) | |
})(window) |
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.1/moment.min.js"></script> |
@import url('https://fonts.googleapis.com/css?family=News+Cycle'); | |
:root { | |
--font: 'News Cycle', Helvetica, sans-serif; | |
--accent: #9ccf5e; | |
--bg: #403638; | |
--transform: translate(-50%, -50%); | |
} | |
// primary layout | |
aside { | |
position: relative; | |
flex: 1; | |
order: 1; | |
} | |
main { | |
position: relative; | |
flex: 1; | |
order: 2; | |
overflow: auto; | |
height: 100vh; | |
section { | |
display: flex; | |
min-height: 100vh; | |
} | |
} | |
svg.logo { | |
position: absolute; | |
pointer-event: none; | |
font-size: 100vmax; | |
width: 1em; | |
height: 400px; | |
top: 50%; | |
left: 50%; | |
will-change: transform; | |
transform: var(--transform); | |
transition: transform 12000ms ease-in-out; | |
mix-blend-mode: overlay; | |
path { | |
fill: var(--accent); | |
transition: fill 3000ms linear; | |
} | |
} | |
// schedule listing | |
main { | |
background: rgba(#001, .8); | |
color: #fff; | |
header { | |
flex: 0 0 auto; | |
writing-mode: vertical-rl; | |
padding: 1em .5em; | |
color: rgba(#001, .4); | |
background: #fff; | |
} | |
ul { | |
list-style: none; | |
margin: 0; | |
padding: 2rem; | |
flex: 1; | |
display: flex; | |
flex-direction: column; | |
justify-content: space-between; | |
} | |
li { | |
position: relative; | |
transition: font-size 300ms ease-in, opacity 300ms ease-in; | |
font-size: .5em; | |
opacity: .2; | |
line-height: 1.2; | |
&.current { | |
font-size: 1em; | |
opacity: 1; | |
&::after { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: -2rem; | |
transform: translateY(-.5rem); | |
border: .5em solid; | |
border-width: 1em 0 1em .625em; | |
border-color: transparent; | |
border-left-color: currentColor; | |
} | |
~ li { | |
opacity: .4; | |
} | |
+ li { | |
font-size: .675em; | |
opacity: .7; | |
} | |
} | |
} | |
} | |
h1 { | |
position: relative; | |
top: .65em; | |
left: .65em; | |
margin: 0; | |
line-height: 1; | |
display: inline; | |
} | |
h3 { | |
margin: 0; | |
text-transform: uppercase; | |
font-weight: 200; | |
font-size: 1.325em; | |
b { | |
font-weight: 900; | |
} | |
} | |
// playground | |
.clock { | |
position: absolute; | |
font-size: 1em; | |
top: 1em; | |
right: 1em; | |
transform: translate(0, calc(-1.3em + var(--y))); | |
transition: transform 300ms ease-out; | |
} | |
small { | |
font-size: .75em; | |
color: var(--accent); | |
transition: color 3000ms linear; | |
small { | |
font-size: .85em; | |
} | |
} | |
// before/after hours | |
main li.after-hours { | |
animation: fade-in 1000ms ease-in 80ms forwards; | |
position: fixed; | |
font-size: 1.2em; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background: rgba(#000, .85); | |
line-height: 1.5; | |
width: 100vw; | |
height: 100vh; | |
display: flex; | |
opacity: 0; | |
// z-index: 1; | |
text-align: center; | |
align-items: center; | |
justify-content: center; | |
} | |
main li.current ~ li.after-hours { | |
display: none; | |
opacity: 0; | |
} | |
// multi-day presentation | |
// main section + section { | |
// ul { | |
// box-shadow: 0 1px 0 0 inset; | |
// } | |
// header { | |
// box-shadow: 0 1px 0 0 inset; | |
// } | |
// } | |
// animations | |
@keyframes fade-in { | |
100% {opacity: 1;} | |
} | |
// demo | |
.control { | |
position: absolute; | |
z-index: 2; | |
bottom: 0; | |
right: 0; | |
font-size: .5em; | |
background: rgba(#000, .7); | |
transform: translateY(calc(100% - 2em)); | |
transition: transform 300ms ease-out; | |
header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
text-transform: uppercase; | |
cursor: pointer; | |
div { | |
padding-left: 1em; | |
} | |
} | |
> div { | |
padding: 1em; | |
} | |
p { | |
margin: 0; | |
+ p { | |
margin-top: 1em; | |
} | |
} | |
input[type=range] { | |
padding: 0; | |
border: 0; | |
width: 15em; | |
vertical-align: middle; | |
border-radius: 1em; | |
background: rgba(#fff, 0.3); | |
font-size: 1rem; | |
line-height: 1; | |
appearance: none; | |
outline: none; | |
transition: all .3s; | |
outline-offset: 0; | |
margin: 0; | |
&::-webkit-slider-thumb { | |
appearance: none; | |
width: 1em; | |
height: 1em; | |
border: none; | |
border-radius: 1em; | |
background: #E2E4E6; | |
background-image: none; | |
transform: scale(2); | |
transition: all .3s; | |
} | |
} | |
button { | |
font: inherit; | |
border: none; | |
color: inherit; | |
width: 2em; | |
height: 2em; | |
font-size: 1em; | |
font-weight: 100; | |
background: rgba(#fff, .4); | |
outline: none; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
line-height: 0; | |
transform: rotate(180deg); | |
} | |
&.show { | |
transform: translateY(0%); | |
button { | |
transform: rotate(0deg); | |
} | |
} | |
} | |
// boring | |
*, *::before, *::after {box-sizing: border-box;} | |
html, body { | |
background: var(--bg); // fixes chrome 58+ mix-blend-mode bug | |
} | |
body { | |
margin: 0; | |
min-height: 100vh; | |
display: flex; | |
flex-flow: row wrap; | |
justify-content: center; | |
overflow: hidden; | |
font-family: var(--font); | |
font-size: calc(1em + 1.5vmin); | |
// background: var(--bg); | |
transition: background 1500ms linear; | |
color: #fff; | |
} | |
// safari fix | |
main header { | |
height: 100%; | |
} | |
// firefox fix | |
main header { | |
width: 0px; | |
padding: 1em 1.25em; | |
line-height: 0; | |
} |