Skip to content
This repository has been archived by the owner on Feb 28, 2022. It is now read-only.

Doomsday - SectionAwards #52

Open
wants to merge 9 commits into
base: doomsday
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions src/components/SectionAwards/SectionAwards.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import SectionAwards from "./SectionAwards"
import API from "@/static/db.json"

const Awards = [
{
total: 2,
text: "BET awards"
},
{
total: 20,
text: "Clio awards"
},
{
total: 13,
text: "Cannes Lions"
},
{
total: 5,
text: "Webby Awards"
},
{
total: 12,
text: "Grammys"
},
{
total: 5,
text: "Latin Grammys"
},
{
total: 8,
text: "Cyclope Awards"
},
{
total: 6,
text: "Shorts Awards"
},
{
total: 2,
text: "Music Video Festival Brazil"
},
{
total: 7,
text: "Berlin Music Video Awards"
}
]

export default {
title: "@dchiamp / SectionAwards"
}

export const Default = () => ({
components: { SectionAwards },
data() {
return {
api: API,
awards: Awards
}
},
template: `<section-awards :awards="awards" title="Awards" />`
})
140 changes: 140 additions & 0 deletions src/components/SectionAwards/SectionAwards.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<template lang="html">
<section
v-intersection-observer="{
threshold: 0
}"
class="section-awards"
@has-intersected="hasIntersected()"
>
<h2 class="title" v-html="title" />

<ul class="awards">
<li v-for="(award, i) in awards" class="award">
<count-up
class="count"
element="span"
:to="award.total"
:start-count="startCount"
:duration="1200"
/>

<p v-html="award.text" />
</li>
</ul>
</section>
</template>

<script>
// Components
import CountUp from "@/components/global/CountUp"

// Directives
import Vue from "vue"
import intersectionObserver from "@/directives/IntersectionObserver"
Vue.directive("intersection-observer", intersectionObserver)

export default {
components: {
CountUp
},
props: {
title: {
type: String,
default: "Awards"
},
awards: {
type: Array,
default: () => []
}
},
data() {
return {
startCount: false
}
},
methods: {
// Start count when component hasIntersected
hasIntersected() {
this.startCount = true
}
}
}
</script>

<style lang="scss" scoped>
.section-awards {
position: relative;
background-color: var(--color-pink);
color: var(--color-black);
padding: 60px 50px;
box-sizing: border-box;

.title {
font-family: var(--font-secondary);
font-size: 75px;
font-weight: 500;
text-transform: uppercase;
-webkit-text-stroke: 1.5px var(--color-pink);
margin: 0;
position: absolute;
top: -47px;
left: 50%;
transform: translate(-50%);
}

.awards {
list-style: none;
margin: 0;
padding: 0;
columns: 2;
max-width: var(--unit-max-width);
margin: 0 auto;
}
.award {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
margin: 10px 0;
.count {
font-family: var(--font-primary);
font-size: 50px;
font-weight: 400;
min-width: 75px;
height: auto;
text-align: center;
}
p {
font-family: var(--font-secondary);
display: inline-block;
font-size: 20px;
font-weight: 500;
text-transform: uppercase;
margin: 0 10px;
}
}

// Breakpoints
@media #{$lt-phone} {
padding: 50px 20px;

.title {
font-size: 30px;
top: -20px;
}
.awards {
columns: 1;
}
.award {
margin: 0 0 10px 0;
.count {
font-size: 20px;
min-width: 30px;
}
p {
font-size: 12px;
}
}
}
}
</style>
62 changes: 62 additions & 0 deletions src/components/global/CountUp.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<template>
<component :is="element" v-text="displayNumber" />
</template>

<script>
export default {
props: {
from: {
type: Number,
default: 0
},
to: {
type: Number,
required: true
},
duration: {
type: Number,
default: 1000 // Duration is in milliseconds
},
element: {
type: String,
default: "div"
},
startCount: {
type: Boolean,
required: true
}
},
data() {
return {
displayNumber: this.from,
countInterval: null
}
},
computed: {
interval() {
return this.duration / (this.to - this.from)
}
},
watch: {
startCount(newVal) {
if (newVal && !this.countInterval) {
this.countInterval = setInterval(
() => this.incrementDisplayNimber(),
this.interval
)
} else this.displayNumber = this.from
}
},
methods: {
beginCount() {},
incrementDisplayNimber() {
this.displayNumber += 1
if (this.displayNumber == this.to) this.clearCountInterval()
},
clearCountInterval() {
clearInterval(this.countInterval)
this.countInterval = null
}
}
}
</script>
101 changes: 101 additions & 0 deletions src/directives/IntersectionObserver.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script>
// Helpers
import _pick from "lodash/pick"

// Setup class
class ObserverState {
// This gets function called automatically
constructor(el, settings, vnode) {
this.settings = settings
this.observer = this.initObserver()
}

// Creates Observer
initObserver() {
return new IntersectionObserver(
this.onIntersection,
this.settings.value
)
}

// This is the function that runs when an Intersection occurs.
// NOTE Intersection happens when it goes out of view too!
// SEE https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
// SEE https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry
onIntersection(entries, observer) {
entries.forEach(function(entry, index) {
// Set up required data
const el = entry.target
const state = el._observerState
const hasClass = el.classList.contains("has-intersected")

// If "once" and el already has class we have nothing to do
if (state.settings.modifiers.once && hasClass) return

// Check if element has passed intersection threshold
if (entry.intersectionRatio < state.settings.value.threshold)
return

// Fire event with IntersectionObserverEntry object
const event = new CustomEvent("has-intersected", { detail: entry })

// Generate random time
let number = 0
if (state.settings.modifiers.stagger) {
number = Math.floor(Math.random() * Math.floor(500))
}

// Stagger time by number
clearTimeout(el.observerTimer)
el.observerTimer = setTimeout(() => {
el.dispatchEvent(event)

// Add a class for convenience
if (entry.isIntersecting) {
el.classList.add("has-intersected")
} else {
el.classList.remove("has-intersected")
}
}, number)
});
}
}

// The Vue directive config
export default {
bind(el, binding) {
// Default settings
let settings = {
value: {
root: null,
rootMargin: "0px",
threshold: 1.0
},
modifiers: {
once: false,
stagger: false
}
}

// Overwrite settings, only keep relevant settings
settings = Object.assign(settings, binding)
settings = _pick(settings, ["value", "modifiers"])

// Start ObserverState constructor, save to el for future access
el._observerState = new ObserverState(el, settings)
},
unbind(el) {
const state = el._observerState
if (state) {
state.observer.disconnect()
delete el._observerState
}
},
inserted(el) {
const state = el._observerState
if (state) {
state.observer.observe(el)
}
}
}
</script>