Skip to content

Commit

Permalink
feat(content): add component SaladBowl
Browse files Browse the repository at this point in the history
  • Loading branch information
crimx committed Apr 14, 2018
1 parent af6e6df commit 4200c19
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 0 deletions.
99 changes: 99 additions & 0 deletions src/content/components/SaladBowl/_style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
$bowl-width: 30px;
$tomato-rotate: 45deg;
$leaf-rotate: 30deg;

:root:root:root:root:root {
.saladict-SaladBowl{
@extend %reset-important;
position: fixed !important;
z-index: $global-zindex-tooltip !important;
overflow: hidden !important;
width: $bowl-width !important;
height: $bowl-width !important;
}

.saladict-SaladBowl_Leaf {
@extend %reset-important;
position: absolute !important;
z-index: 1 !important;
top: 50 * $bowl-width / 1024 !important;
left: 600 * $bowl-width / 1024 !important;
width: $bowl-width * 729 / 1024 * 0.6 !important;
height: $bowl-width * 0.6 !important;
transform: rotate($leaf-rotate) !important;
}

.saladict-SaladBowl_Orange {
@extend %reset-important;
position: absolute !important;
z-index: 2 !important;
top: 0 * $bowl-width / 1024 !important;
left: 195 * $bowl-width / 1024 !important;
width: $bowl-width * 0.6 !important;
height: $bowl-width * 0.6 !important;
}

.saladict-SaladBowl_Tomato {
@extend %reset-important;
position: absolute !important;
z-index: 3 !important;
top: 250 * $bowl-width / 1024 !important;
left: -117 * $bowl-width / 1024 !important;
width: $bowl-width * 0.6 !important;
height: $bowl-width * 513 / 1024 * 0.6 !important;
transform: rotate($tomato-rotate) !important;
}

.saladict-SaladBowl_Bowl {
@extend %reset-important;
position: absolute !important;
z-index: 4 !important;
bottom: 0 !important;
left: 0 !important;
width: $bowl-width !important;
height: $bowl-width * 530 / 1024 !important;
}
}

/*-----------------------------------------------*\
States
\*-----------------------------------------------*/

.saladict-SaladBowl:hover {
.saladict-SaladBowl_Leaf {
animation: saladict-SaladBowl_Leaf-shake 1s infinite linear;
}
.saladict-SaladBowl_Orange {
animation: saladict-SaladBowl_Orange-spin 1s infinite linear;
}
.saladict-SaladBowl_Tomato {
animation: saladict-SaladBowl_Tomato-shake 0.7s infinite linear;
}
}

@keyframes saladict-SaladBowl_Leaf-shake {
0% { transform: translate( 2px, 1px) rotate($leaf-rotate + 0deg); }
10% { transform: translate(-1px, -2px) rotate($leaf-rotate - 1deg); }
20% { transform: translate(-2px, 0 ) rotate($leaf-rotate + 1deg); }
30% { transform: translate( 0 , 2px) rotate($leaf-rotate + 0deg); }
40% { transform: translate( 1px, -1px) rotate($leaf-rotate + 1deg); }
50% { transform: translate(-1px, 2px) rotate($leaf-rotate - 1deg); }
60% { transform: translate(-2px, 1px) rotate($leaf-rotate + 0deg); }
70% { transform: translate( 2px, 1px) rotate($leaf-rotate - 1deg); }
80% { transform: translate(-1px, -1px) rotate($leaf-rotate + 1deg); }
90% { transform: translate( 2px, 2px) rotate($leaf-rotate + 0deg); }
100% { transform: translate( 1px, -2px) rotate($leaf-rotate - 1deg); }
}

@keyframes saladict-SaladBowl_Orange-spin {
from { transform: rotate( 0deg); }
to { transform: rotate(360deg); }
}

@keyframes saladict-SaladBowl_Tomato-shake {
0% { transform: rotate($tomato-rotate - 10deg); }
30% { transform: rotate($tomato-rotate + 0deg); }
60% { transform: rotate($tomato-rotate + 10deg); }
90% { transform: rotate($tomato-rotate + 0deg); }
100% { transform: rotate($tomato-rotate - 5deg); }
}
84 changes: 84 additions & 0 deletions src/content/components/SaladBowl/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { Motion, spring, presets, OpaqueConfig } from 'react-motion'

interface SaladBowlProps {
shouldShow: boolean
mouseX: number
mouseY: number
}

interface SaladBowlState {
}

export default class SaladBowl extends React.Component<SaladBowlProps, SaladBowlState> {
root = document.body
el = document.createElement('div')
isMount = false

/** reusable tuple */
_calcPositionShell: [number, number] = [0, 0]

calcPosition (mouseX: number, mouseY: number): [number, number] {
// icon position
// +-------+
// | |
// | | 30px
// 60px +-------+
// | 30px
// |
// 40px |
// +-------+
// cursor
const tuple = this._calcPositionShell
tuple[0] = mouseX + 40 + 30 > window.innerWidth ? mouseX - 40 - 30 : mouseX + 40
tuple[1] = mouseY > 60 ? mouseY - 60 : mouseY + 60 - 30
return tuple
}

componentWillUnmount () {
this.root.removeChild(this.el)
}

renderBowl (pos) {
const style = {
transform: `translate3d(${pos.x}px, ${pos.y}px, 0) scale(${pos.scale})`
}
return (
<div className='saladict-SaladBowl' style={style}>
<img className='saladict-SaladBowl_Leaf' src={require('@/assets/leaf.svg')} />
<img className='saladict-SaladBowl_Orange' src={require('@/assets/orange.svg')} />
<img className='saladict-SaladBowl_Tomato' src={require('@/assets/tomato.svg')} />
<img className='saladict-SaladBowl_Bowl' src={require('@/assets/bowl.svg')} />
</div>
)
}

render () {
let [x, y]: (number | OpaqueConfig)[] = this.calcPosition(this.props.mouseX, this.props.mouseY)
let scale: number | OpaqueConfig = 0

if (this.props.shouldShow) {
if (!this.isMount) {
this.root.appendChild(this.el)
this.isMount = true
scale = spring(1, presets.wobbly)
} else {
// only animate position when the bowl is already visible
x = spring(x, presets.gentle)
y = spring(y, presets.gentle)
scale = 1
}
} else {
if (this.isMount) {
this.root.removeChild(this.el)
this.isMount = false
}
}

return ReactDOM.createPortal(
<Motion style={{ x, y, scale }}>{this.renderBowl}</Motion>,
this.el,
)
}
}

0 comments on commit 4200c19

Please sign in to comment.