Skip to content
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Rumfoords
# Code Study Boilerplate

**Local Development**

Expand Down
7 changes: 0 additions & 7 deletions app.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
<!--
/* WEBSITE CREDITS */

Website Design / Development: CTHDRL
Contact: build@cthdrl.co
Site: https://cthdrl.co
-->
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
Expand Down
15 changes: 5 additions & 10 deletions assets/scss/_base.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@import 'vars';
@import url('https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400;0,700;1,400;1,700&family=Poppins:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400;0,700;1,400;1,700&family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap');

html {
font-size: 16px;
Expand All @@ -12,7 +12,7 @@ body {
--black: #000000;
--white: #ffffff;

--font-sans: 'Poppins', sans-serif;
--font-sans: 'IBM Plex Sans', sans-serif;
--font-serif: 'EB Garamond', serif;

@include fontSize(16px);
Expand Down Expand Up @@ -107,20 +107,15 @@ a {
path {
transition: fill 0.3s;
}

@include hover {
color: var(--white);

path {
fill: var(--white);
}
&:hover {
text-decoration: underline;
}
}

.contained {
padding-right: var(--margin);
padding-left: var(--margin);
// max-width: 1380px;
max-width: 1200px;
margin-right: auto;
margin-left: auto;
}
Expand Down
229 changes: 229 additions & 0 deletions components/Clippy.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
<template>
<div class="clippy" :style="textStyles">
<!-- HIDDEN DUMMY SVG -->
<slot v-if="!defaultSlot.text" />

<!-- RENDERED SVG -->
<svg
ref="renderSvg"
:class="{ 'text-svg': defaultSlot.text }"
:width="svgWidth"
:height="svgHeight"
viewBox="0 0 28 29"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<clipPath
:id="pathID"
:clipPathUnits="clipPathUnits"
:transform="clipTransform"
>
<!-- TEXT -->
<text
v-if="defaultSlot.text"
x="0"
:y="fontSize"
ref="text"
>
{{ defaultSlot.text }}
</text>

<!-- SVG PATH -->
<component
v-for="(child, i) in svgChildren"
:is="child.tagName"
v-bind="getDrawAttribute(child)"
:key="i"
/>
</clipPath>
</defs>
</svg>

<!-- BACKGROUND -->
<transition name="fade">
<div
v-if="pathID"
:class="['clip-container', { 'show-on-hover': showOnHover }]"
:style="clipStyles"
>
<transition name="fade">
<img v-if="!videoSrc.length" :src="imageSrc" key="image" />
<video
v-else
:src="videoSrc"
muted
autoplay
loop
key="video"
/>
</transition>

<div class="color-bg" />
</div>
</transition>
</div>
</template>

<script>
import _camelCase from 'lodash/camelCase'

/**
* Text or SVG element can be slotted in to create clipping effect
*
* SVG must have an id set, and contain only path elements
*/

export default {
props: {
imageSrc: {
type: String,
default: '',
required: true,
},
videoSrc: {
type: String,
default: '',
},
showOnHover: {
type: Boolean,
default: false,
},
},
data() {
return {
pathID: null,
fontSize: '12px',
svgEl: {},
svgChildren: [],
}
},
mounted() {
// if the slot contains text
if (this.defaultSlot.text) {
// set unique id of clipPath
this.pathID = _camelCase(this.defaultSlot.text)
// get rendered length of text and set width of final svg
const textLength = this.$refs.text.getComputedTextLength()
this.$refs.renderSvg.style.width = textLength + 'px'
// set inherited font-size to data
this.fontSize = window
.getComputedStyle(this.$el)
.getPropertyValue('font-size')
}
// if the slot contains an element
else if (this.defaultSlot.elm) {
this.svgEl = this.defaultSlot.elm
// hide the slotted svg
this.svgEl.style.display = 'none'

this.svgChildren = [...this.svgEl.children]
// set unique id of clipPath
this.pathID = 'clip' + this.svgEl.id
}
},
computed: {
defaultSlot() {
return this.$slots.default[0]
},
clipStyles() {
return {
'--path-url': `url('#${this.pathID}')`,
}
},
textStyles() {
return {
'--set-font-size': this.fontSize,
}
},
clipPathUnits() {
return this.defaultSlot.text
? 'userSpaceOnUse'
: 'objectBoundingBox'
},
svgWidth() {
if (!this.svgEl.getAttribute) return 0
return this.svgEl.getAttribute('width')
},
svgHeight() {
if (!this.svgEl.getAttribute) return 0
return this.svgEl.getAttribute('height')
},
xScale() {
return 1 / parseInt(this.svgWidth)
},
yScale() {
return 1 / parseInt(this.svgHeight)
},
clipTransform() {
if (!this.svgEl.getAttribute) return ''
return `scale(${this.xScale}, ${this.yScale})`
},
},
methods: {
getDrawAttribute(el) {
return { d: el.getAttribute('d') }
},
},
}
</script>

<style lang="scss">
.clippy {
position: relative;
display: inline-block;

svg {
width: 100%;
height: auto;
}
.text-svg {
height: var(--set-font-size);

text {
font-size: inherit;
font-weight: inherit;
}
}
.clip-container {
@include fill;
width: 100%;
height: 100%;
-webkit-clip-path: var(--path-url);
clip-path: var(--path-url);

.color-bg {
opacity: 0;
}
video,
img {
@include fill;
width: 100%;
height: 100%;
object-fit: cover;
}
}

.show-on-hover {
.color-bg {
opacity: 1;
@include fill;
background: currentColor;
transition: opacity 300ms ease-in-out;
}
video,
img {
opacity: 0;
transition: opacity 300ms ease-in-out;
}
}
&:hover .show-on-hover {
.color-bg {
opacity: 0;
}
video,
img {
opacity: 1;
}
}
}
</style>
Loading