template精简

master
toesbieya 6 years ago
parent 78ce11a336
commit 6e66965193
  1. 2
      vue/template/src/directive/index.js
  2. 184
      vue/template/src/directive/ripple/index.js
  3. 37
      vue/template/src/directive/ripple/style.scss
  4. 34
      vue/template/src/globalMethod/ImageViewer/index.js
  5. BIN
      vue/template/src/globalMethod/Verify/image/btn_move.png
  6. BIN
      vue/template/src/globalMethod/Verify/image/btn_stop.png
  7. BIN
      vue/template/src/globalMethod/Verify/image/icon_close.png
  8. BIN
      vue/template/src/globalMethod/Verify/image/icon_info.png
  9. BIN
      vue/template/src/globalMethod/Verify/image/icon_refresh_bottom.png
  10. BIN
      vue/template/src/globalMethod/Verify/image/icon_refresh_top.png
  11. BIN
      vue/template/src/globalMethod/Verify/image/loading.png
  12. BIN
      vue/template/src/globalMethod/Verify/image/track.png
  13. 44
      vue/template/src/globalMethod/Verify/index.js
  14. 330
      vue/template/src/globalMethod/Verify/main.vue
  15. 216
      vue/template/src/globalMethod/Verify/style.scss
  16. 7
      vue/template/src/globalMethod/index.js
  17. 2
      vue/template/src/main.js
  18. 44
      vue/template/src/plugin/canvasAnimation/firework/Particle.js
  19. 20
      vue/template/src/plugin/canvasAnimation/firework/Rocket.js
  20. 20
      vue/template/src/plugin/canvasAnimation/firework/Time.js
  21. 42
      vue/template/src/plugin/canvasAnimation/firework/Trail.js
  22. 22
      vue/template/src/plugin/canvasAnimation/firework/Vector.js
  23. 141
      vue/template/src/plugin/canvasAnimation/firework/index.js
  24. 95
      vue/template/src/plugin/canvasAnimation/godrays/index.js
  25. 261
      vue/template/src/plugin/canvasAnimation/godrays/original.js
  26. 30
      vue/template/src/plugin/canvasAnimation/moveFollowMouse/Particle.js
  27. 71
      vue/template/src/plugin/canvasAnimation/moveFollowMouse/index.js
  28. 90
      vue/template/src/plugin/canvasAnimation/particleBall/Particle.js
  29. 74
      vue/template/src/plugin/canvasAnimation/particleBall/Vector.js
  30. 91
      vue/template/src/plugin/canvasAnimation/particleBall/index.js
  31. 13
      vue/template/src/plugin/canvasAnimation/particleBall/util.js
  32. 50
      vue/template/src/plugin/canvasAnimation/particleNetwork/Particle.js
  33. 207
      vue/template/src/plugin/canvasAnimation/particleNetwork/index.js
  34. 160
      vue/template/src/plugin/canvasAnimation/particleWave/index.js
  35. 29
      vue/template/src/plugin/canvasAnimation/reflectRain/RainDrop.js
  36. 11
      vue/template/src/plugin/canvasAnimation/reflectRain/Vector.js
  37. 85
      vue/template/src/plugin/canvasAnimation/reflectRain/index.js
  38. 18
      vue/template/src/plugin/canvasAnimation/sparkRain/Spark.js
  39. 89
      vue/template/src/plugin/canvasAnimation/sparkRain/index.js
  40. 3
      vue/template/src/plugin/canvasAnimation/sparkRain/util.js
  41. 81
      vue/template/src/plugin/canvasAnimation/sunlight/Particle.js
  42. 97
      vue/template/src/plugin/canvasAnimation/sunlight/index.js
  43. 7
      vue/template/src/router/module/example/child/directive.js
  44. 12
      vue/template/src/router/module/example/child/globalMethod.js
  45. 14
      vue/template/src/store/module/app.js
  46. 3
      vue/template/src/view/_app/login/LoginForm.vue
  47. 49
      vue/template/src/view/_app/login/SetAnimation.vue
  48. 35
      vue/template/src/view/_app/login/index.vue
  49. 22
      vue/template/src/view/_app/login/style.scss
  50. 57
      vue/template/src/view/example/directive/ripplePage.vue
  51. 33
      vue/template/src/view/example/globalMethod/imageViewerPage.vue
  52. 25
      vue/template/src/view/example/globalMethod/verifyPage.vue

@ -1,7 +1,5 @@
import Vue from 'vue'
import dragDialog from './dragDialog'
import ripple from './ripple'
Vue.directive('drag-dialog', dragDialog)
Vue.directive('ripple', ripple)

@ -1,184 +0,0 @@
//从quasar处搬运 https://quasar.dev/vue-directives/material-ripple
import './style.scss'
let lastKeyCompositionStatus = false
function isKeyCode(evt, keyCodes) {
return (
lastKeyCompositionStatus === true ||
evt !== Object(evt) ||
evt.isComposing === true ||
evt.qKeyEvent === true
)
? false
: [].concat(keyCodes).includes(evt.keyCode)
}
function css(element, css) {
let style = element.style
Object.keys(css).forEach(prop => {
style[prop] = css[prop]
})
}
function position(e) {
if (e.touches && e.touches[0]) {
e = e.touches[0]
}
else if (e.changedTouches && e.changedTouches[0]) {
e = e.changedTouches[0]
}
else if (e.targetTouches && e.targetTouches[0]) {
e = e.targetTouches[0]
}
return {
top: e.clientY,
left: e.clientX
}
}
function stop(e) {
e.stopPropagation()
}
const listenOpts = {
hasPassive: false,
passiveCapture: true,
notPassiveCapture: true
}
function showRipple(evt, el, ctx, forceCenter) {
ctx.modifiers.stop === true && stop(evt)
let {center, color} = ctx.modifiers
center = center === true || forceCenter === true
const
node = document.createElement('span'),
innerNode = document.createElement('span'),
pos = position(evt),
{left, top, width, height} = el.getBoundingClientRect(),
diameter = Math.sqrt(width * width + height * height),
radius = diameter / 2,
centerX = `${(width - diameter) / 2}px`,
x = center ? centerX : `${pos.left - left - radius}px`,
centerY = `${(height - diameter) / 2}px`,
y = center ? centerY : `${pos.top - top - radius}px`
innerNode.className = 'q-ripple__inner'
css(innerNode, {
height: `${diameter}px`,
width: `${diameter}px`,
transform: `translate3d(${x},${y},0) scale3d(.2,.2,1)`,
opacity: 0
})
node.className = `q-ripple${color ? ' text-' + color : ''}`
node.setAttribute('dir', 'ltr')
node.appendChild(innerNode)
el.appendChild(node)
const abort = () => {
node.remove()
window.clearTimeout(timer)
}
ctx.abort.push(abort)
let timer = window.setTimeout(() => {
innerNode.classList.add('q-ripple__inner--enter')
innerNode.style.transform = `translate3d(${centerX},${centerY},0) scale3d(1,1,1)`
innerNode.style.opacity = 0.2
timer = window.setTimeout(() => {
innerNode.classList.remove('q-ripple__inner--enter')
innerNode.classList.add('q-ripple__inner--leave')
innerNode.style.opacity = 0
timer = window.setTimeout(() => {
node.remove()
ctx.abort.splice(ctx.abort.indexOf(abort), 1)
}, 275)
}, 250)
}, 50)
}
function updateCtx(ctx, {value, modifiers, arg}) {
ctx.enabled = value !== false
if (ctx.enabled === true) {
ctx.modifiers = Object(value) === value
? {
stop: value.stop === true || modifiers.stop === true,
center: value.center === true || modifiers.center === true,
color: value.color || arg,
keyCodes: [].concat(value.keyCodes || 13)
}
: {
stop: modifiers.stop,
center: modifiers.center,
color: arg,
keyCodes: [13]
}
}
}
export default {
name: 'ripple',
inserted(el, binding) {
const ctx = {
modifiers: {},
abort: [],
click(evt) {
if (ctx.enabled === true && evt.qSkipRipple !== true && evt.clientX >= 0) {
showRipple(evt, el, ctx, evt.qKeyEvent === true)
}
},
keyup(evt) {
if (
ctx.enabled === true &&
evt.qSkipRipple !== true &&
isKeyCode(evt, ctx.modifiers.keyCodes) === true
) {
showRipple(evt, el, ctx, true)
}
}
}
updateCtx(ctx, binding)
if (el.__qripple) {
el.__qripple_old = el.__qripple
}
const computed = window.getComputedStyle(el)
if (computed && computed.position === 'static') {
el.style.position = 'relative'
el.dataset.previousPosition = 'static'
}
el.__qripple = ctx
el.addEventListener('click', ctx.click, listenOpts.passive)
el.addEventListener('keyup', ctx.keyup, listenOpts.passive)
},
update(el, binding) {
el.__qripple !== void 0 && updateCtx(el.__qripple, binding)
},
unbind(el) {
const ctx = el.__qripple_old || el.__qripple
if (ctx !== void 0) {
ctx.abort.forEach(fn => {
fn()
})
el.removeEventListener('click', ctx.click, listenOpts.passive)
el.removeEventListener('keyup', ctx.keyup, listenOpts.passive)
delete el[el.__qripple_old ? '__qripple_old' : '__qripple']
}
}
}

@ -1,37 +0,0 @@
.q-ripple {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
color: inherit;
border-radius: inherit;
z-index: 0;
pointer-events: none;
overflow: hidden;
contain: strict;
&__inner {
position: absolute;
top: 0;
left: 0;
opacity: 0;
color: inherit;
border-radius: 50%;
background: currentColor;
pointer-events: none;
will-change: transform, opacity;
&--enter {
transition: transform .225s cubic-bezier(.4, 0, .2, 1), opacity .1s cubic-bezier(.4, 0, .2, 1)
}
&--leave {
transition: opacity .25s cubic-bezier(.4, 0, .2, 1)
}
}
}

@ -1,34 +0,0 @@
let insertEl = false
const image = function ({index = 0, urlList}) {
if (!window.lightGallery || !Array.isArray(urlList) || urlList.length < 1) {
return
}
if (!insertEl) {
const div = document.createElement('div')
div.setAttribute('id', 'light-gallery-el')
div.style.display = 'none'
document.body.appendChild(div)
div.addEventListener(
'onCloseAfter',
() => window.lgData[div.getAttribute('lg-uid')].destroy(true),
false
)
insertEl = true
}
window.lightGallery(document.getElementById('light-gallery-el'), {
index,
download: false,
dynamic: true,
dynamicEl: urlList.map(src => ({src, thumb: src}))
})
}
image.close = function () {
if (!insertEl) return
window.lgData[insertEl.getAttribute('lg-uid')].destroy(true)
}
export default image

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 961 B

@ -1,44 +0,0 @@
import Vue from 'vue'
import Main from './main.vue'
import {isEmpty} from "@/util"
let Constructor = Vue.extend(Main)
export default function ({left, top} = {}) {
const instance = new Constructor()
const position = getPosition({height: instance.height + 130, width: instance.width + 20, left, top})
instance.positionTop = position.top
instance.positionLeft = position.left
instance.$mount()
document.body.appendChild(instance.$el)
instance.visible = true
return new Promise((resolve, reject) => {
instance.resolve = resolve
instance.reject = reject
})
}
function getPosition({height, width, left, top} = {}) {
const windowHeight = window.innerHeight
const windowWidth = window.innerWidth
//若任一位置信息为空,直接返回居中位置
if (isEmpty(left, top)) {
return {
top: center(windowHeight, height),
left: center(windowWidth, width)
}
}
//判断位置是否超出屏幕外
if (windowWidth < left + width) left = center(windowWidth, width)
if (windowHeight < top + height) top = center(windowHeight, height)
return {left, top}
}
function center(full, part) {
return Math.round((full - part) / 2)
}

@ -1,330 +0,0 @@
<template>
<transition :name="transition" @after-leave="handleAfterLeave">
<div
v-show="visible"
class="puzzle-container"
:class="{'puzzle-shake':stat==='fail'}"
:style="`left: ${positionLeft}px;top: ${positionTop}px;`"
>
<div class="puzzle-view" :style="`width:${width}px;height:${height}px;`">
<div v-show="!image.loading" style="width: 100%;height: 100%">
<img :src="image.src"/>
<canvas ref="puzzleBox" :height="height" :width="width"/>
<div class="puzzle-lost-box" :style="`left:${leftNum}px`">
<canvas ref="puzzleShadow" :style="sliderStyle" :height="height" :width="width"/>
<canvas ref="puzzleLost" :style="sliderStyle" :height="height" :width="width"/>
</div>
</div>
<transition name="fade">
<div v-show="image.loading" class="puzzle-image-loading">
<div class="puzzle-image-loading__icon"/>
<div class="puzzle-image-loading__text">加载中...</div>
</div>
</transition>
<div :class="resultTipClass">
{{ stat === 'success' ? '验证通过' : '请正确拼合图像' }}
</div>
</div>
<div class="slider-container">
<div class="slider-track">
<div class="slider-tip" :style="stat==='ready'?'opacity: 1':''">拖动滑块完成拼图</div>
</div>
<div class="slider-btn" :style="sliderBtnStyle" @mousedown="moveStart" @touchstart="moveStart"/>
</div>
<div class="puzzle-footer">
<div class="puzzle-small">
<a class="puzzle-text-container puzzle-close" @click="close">
<div class="puzzle-text-tip">关闭验证</div>
</a>
<a class="puzzle-text-container puzzle-refresh" @click="refresh">
<div class="puzzle-text-tip">刷新验证</div>
</a>
</div>
</div>
</div>
</transition>
</template>
<script>
import BtnStopImage from './image/btn_stop.png'
import BtnMoveImage from './image/btn_move.png'
/*
* https://github.com/Kevin-269581661/vue-puzzle-verification
* 网上搬的例子实际开发建议用极验
* */
export default {
name: "PuzzleVerify",
props: {
//transition.scss
transition: {
type: String,
default: 'scale'
},
//
width: {
type: Number,
default: 260
},
height: {
type: Number,
default: 160
},
//
blockSize: {
type: Number,
default: 40
},
//
deviation: {
type: Number,
default: 4
},
//
padding: {
type: Number,
default: 80
}
},
data() {
return {
visible: false,
stat: 'ready',//ready,move,success,fail,error
positionLeft: 0,
positionTop: 0,
resolve: () => ({}),
reject: () => ({}),
moveStartAtX: null,
randomX: null,
randomY: null,
leftNum: 0,
image: {
src: '',
loading: true
},
sliderStyle: {
left: '0',
transition: 'inherit'
}
}
},
computed: {
resultTipClass() {
const showResultTip = ['success', 'fail'].includes(this.stat)
return ['verify-result', `verify-${this.stat}`, showResultTip ? 'show-result' : '']
},
sliderBtnStyle() {
return {
...this.sliderStyle,
'background-image': `url(${this.stat === 'move' ? BtnMoveImage : BtnStopImage})`
}
}
},
methods: {
close() {
this.visible = false
this.clear()
this.reject()
},
refresh() {
this.initImage().then(this.initCanvas)
},
initCanvas() {
this.clear()
let MinX = this.padding + this.blockSize
let MaxX = this.width - this.padding - 2 * this.blockSize - this.blockSize / 6
let MaxY = this.padding
let MinY = this.height - this.padding - this.blockSize - this.blockSize / 6
this.randomX = Math.round(Math.random() * MaxX + MinX)
this.randomY = Math.round(Math.random() * MaxY + MinY)
let X = this.randomX
let Y = this.randomY
this.leftNum = -X + 10
let d = this.blockSize / 3
let c = this.$refs.puzzleBox
let c_l = this.$refs.puzzleLost
let c_s = this.$refs.puzzleShadow
let ctx = c.getContext("2d")
let ctx_l = c_l.getContext("2d")
let ctx_s = c_s.getContext("2d")
ctx.globalCompositeOperation = "xor"
ctx.shadowBlur = 10
ctx.shadowColor = "#fff"
ctx.shadowOffsetX = 3
ctx.shadowOffsetY = 3
ctx.fillStyle = "rgba(0,0,0,0.7)"
ctx.beginPath()
ctx.lineWidth = 1
ctx.strokeStyle = "rgba(0,0,0,0)"
ctx.moveTo(X, Y)
ctx.lineTo(X + d, Y)
ctx.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y)
ctx.lineTo(X + 3 * d, Y)
ctx.lineTo(X + 3 * d, Y + d)
ctx.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d)
ctx.lineTo(X + 3 * d, Y + 3 * d)
ctx.lineTo(X, Y + 3 * d)
ctx.closePath()
ctx.stroke()
ctx.fill()
this.drawImage(img => ctx_l.drawImage(img, 0, 0, this.width, this.height))
ctx_l.beginPath()
ctx_l.strokeStyle = "rgba(0,0,0,0)"
ctx_l.moveTo(X, Y)
ctx_l.lineTo(X + d, Y)
ctx_l.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y)
ctx_l.lineTo(X + 3 * d, Y)
ctx_l.lineTo(X + 3 * d, Y + d)
ctx_l.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d)
ctx_l.lineTo(X + 3 * d, Y + 3 * d)
ctx_l.lineTo(X, Y + 3 * d)
ctx_l.closePath()
ctx_l.stroke()
ctx_l.shadowBlur = 10
ctx_l.shadowColor = "black"
ctx_l.clip()
ctx_s.beginPath()
ctx_s.lineWidth = 1
ctx_s.strokeStyle = "rgba(0,0,0,0)"
ctx_s.moveTo(X, Y)
ctx_s.lineTo(X + d, Y)
ctx_s.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y)
ctx_s.lineTo(X + 3 * d, Y)
ctx_s.lineTo(X + 3 * d, Y + d)
ctx_s.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d)
ctx_s.lineTo(X + 3 * d, Y + 3 * d)
ctx_s.lineTo(X, Y + 3 * d)
ctx_s.closePath()
ctx_s.stroke()
ctx_s.shadowBlur = 20
ctx_s.shadowColor = "black"
ctx_s.fill()
},
initImage() {
const imageListTemp = [
"https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=4018557288,1217151095&fm=26&gp=0.jpg",
"https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3252521864,872614242&fm=26&gp=0.jpg",
"https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2534506313,1688529724&fm=26&gp=0.jpg"
]
this.image.loading = true
let imgRandomIndex = Math.round(Math.random() * (imageListTemp.length - 1))
this.image.src = imageListTemp[imgRandomIndex]
return new Promise(resolve => resolve())
},
drawImage(fun) {
let img = new Image()
img.src = this.image.src
img.onload = () => {
fun(img)
this.image.loading = false
}
},
clear() {
this.stat = 'ready'
let c = this.$refs.puzzleBox
let c_l = this.$refs.puzzleLost
let c_s = this.$refs.puzzleShadow
c.setAttribute("height", c.getAttribute("height"))
c_l.setAttribute("height", c.getAttribute("height"))
c_s.setAttribute("height", c.getAttribute("height"))
},
moveStart(e) {
if (this.stat !== 'ready') return
e.preventDefault()
e.stopPropagation()
this.moveStartAtX = e.type === 'mousedown' ? e.pageX : e.changedTouches[0].pageX
this.addListener()
},
moving(e) {
this.stat = 'move'
const pageX = e.type === 'mousemove' ? e.pageX : e.changedTouches[0].pageX
const distance = pageX - this.moveStartAtX
if (this.moveStartAtX === null
|| distance < 0
|| distance > this.width - this.blockSize) {
return
}
this.sliderStyle.left = distance + "px"
this.sliderStyle.transition = "inherit"
},
moveEnd(e) {
const pageX = e.type === 'mouseup' ? e.pageX : e.changedTouches[0].pageX
const distance = pageX - this.moveStartAtX
const ver_Num = this.randomX - 10
const minLeft = ver_Num - this.deviation
const maxLeft = ver_Num + this.deviation
if (this.moveStartAtX !== null) {
const success = maxLeft > distance && distance > minLeft
this.stat = success ? 'success' : 'fail'
window.setTimeout(() => {
this.stat = 'ready'
if (!success) return this.initCanvas()
this.resolve()
this.close()
}, success ? 500 : 1000)
}
window.setTimeout(() => {
this.sliderStyle.left = '0'
this.sliderStyle.transition = "left 0.5s"
}, 1000)
this.moveStartAtX = null
this.removeListener()
},
addListener() {
if (this.image.loading) return
window.addEventListener("mousemove", this.moving)
window.addEventListener("mouseup", this.moveEnd)
window.addEventListener("touchmove", this.moving)
window.addEventListener("touchend", this.moveEnd)
},
removeListener() {
window.removeEventListener("mousemove", this.moving)
window.removeEventListener("mouseup", this.moveEnd)
window.addEventListener("touchmove", this.moving)
window.addEventListener("touchend", this.moveEnd)
},
handleAfterLeave() {
this.$destroy(true)
this.$el.parentNode.removeChild(this.$el)
}
},
mounted() {
this.refresh()
},
}
</script>
<style lang="scss" src="./style.scss"></style>

@ -1,216 +0,0 @@
@import "~@/asset/style/variables.scss";
.puzzle-container {
position: absolute;
z-index: 1000;
box-shadow: 0 0 10px #cccccc;
border: 1px solid #cccccc;
background: #ffffff;
.puzzle-text-container {
&:hover {
overflow: visible !important;
}
.puzzle-text-tip {
position: absolute;
top: -32px;
left: 10px;
border-radius: 2px;
padding: 0 4px;
height: 22px;
min-width: 50px;
line-height: 22px;
background-color: #5F5F5F;
white-space: nowrap;
font-size: 12px;
text-align: center;
color: white;
&::before {
display: block;
position: absolute;
bottom: -6px;
left: 0;
content: '';
border-style: solid;
border-width: 4px 6px;
border-color: #5F5F5F transparent transparent #5F5F5F;
width: 0;
height: 0;
}
}
}
.puzzle-view {
position: relative;
overflow: hidden;
margin: 10px 10px 0;
.puzzle-image-loading {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: #e5e5e5;
padding-top: 10%;
&__icon {
/*background-size: 764.70588%;
background-position: 0 70.70218%;*/
background-image: url(./image/loading.png);
margin: 11% auto 10px;
width: 34px;
height: 26px;
overflow: hidden;
}
&__text {
text-align: center;
font-size: 14px;
margin-bottom: 1%;
color: #b2b2b2;
}
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
.puzzle-lost-box {
position: absolute;
left: 0;
top: 0;
}
canvas {
position: absolute;
left: 0;
top: 0;
z-index: 22;
}
.verify-result {
position: absolute;
z-index: 999;
color: white;
height: 24px;
line-height: 24px;
left: 0;
bottom: -25px;
font-size: 14px;
width: 100%;
transition: bottom 0.3s;
text-indent: 16px;
&.verify-success {
background-color: $--color-success
}
&.verify-fail {
background-color: $--color-danger
}
&.show-result {
bottom: 0;
}
}
}
.slider-container {
position: relative;
margin: 5.39% 3.24%;
width: 93.52%;
padding: 0 0 13.67% 0;
background-color: #ffffff;
background-image: url(./image/track.png);
.slider-track {
position: absolute;
top: 50%;
margin: -19px 0 0 0;
padding: 0 0 0 25%;
.slider-tip {
position: relative;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity .5s;
line-height: 38px;
font-size: 14px;
text-align: center;
white-space: nowrap;
color: #88949d;
}
}
.slider-btn {
position: absolute;
margin: -4.62% 0 0 -2.31%;
width: 25.38%;
padding: 0 0 25.38% 0;
cursor: pointer;
}
}
.puzzle-footer {
position: relative;
border-top: 1px solid #EEEEEE;
width: 100%;
margin: 0;
padding: 0 0 17.27% 0;
.puzzle-small {
position: absolute;
left: 5.1%;
top: 50%;
margin-top: -4.13%;
padding: 0 0 8.27% 0;
width: 40.45%;
height: 0;
.puzzle-close, .puzzle-refresh {
margin-left: 8.9%;
width: 17.8%;
cursor: pointer;
text-decoration: none;
vertical-align: top;
position: relative;
display: inline-block;
height: 0;
padding-bottom: 17.8%;
overflow: hidden;
}
.puzzle-close {
margin-left: 0;
background-image: url(./image/icon_close.png);
}
.puzzle-refresh {
background: url(./image/icon_refresh_top.png) top center no-repeat,
url(./image/icon_refresh_bottom.png) center bottom no-repeat;
}
}
}
}
.puzzle-shake {
animation: shake 0.2s linear 3 both;
}
@keyframes shake {
25% {
transform: translate3d(-6px, 0, 0)
}
75% {
transform: translate3d(6px, 0, 0)
}
100% {
transform: translate3d(0, 0, 0)
}
}

@ -1,7 +0,0 @@
import ImageViewer from './ImageViewer'
import Verify from './Verify'
export default function (Vue) {
Vue.prototype.$image = ImageViewer
Vue.prototype.$verify = Verify
}

@ -8,12 +8,10 @@ import router from '@/router'
import '@/asset/icon'
import '@/directive'
import filters from './filter'
import globalMethod from '@/globalMethod'
Vue.use(Element)
Vue.use(ElementPersonal)
Vue.use(filters)
Vue.use(globalMethod)
Vue.config.productionTip = false

@ -1,44 +0,0 @@
import Vector from "./Vector"
import Time from "./Time"
export default class Particle {
constructor(position, velocity = new Vector(), color = 'white', radius = 1, lifetime = 1, mass = 1) {
this.position = position
this.velocity = velocity
this.color = color
this.radius = radius
this.lifetime = lifetime
this.mass = mass
this.isInCanvas = true
this.createdOn = Time.now()
}
update(time) {
if (!this.getRemainingLifetime()) return
this.velocity.add(Particle.GRAVITATION.clone().multiplyScalar(this.mass))
this.position.add(this.velocity.clone().multiplyScalar(time.delta))
}
render(canvas, context) {
const remainingLifetime = this.getRemainingLifetime()
if (!remainingLifetime) return
const radius = this.radius * remainingLifetime
context.globalAlpha = remainingLifetime
context.globalCompositeOperation = 'lighter'
context.fillStyle = this.color
context.beginPath()
context.arc(this.position.x, this.position.y, radius, 0, Math.PI * 2)
context.fill()
}
getRemainingLifetime() {
const elapsedLifetime = Time.now() - this.createdOn
return Math.max(0, this.lifetime - elapsedLifetime) / this.lifetime
}
}

@ -1,20 +0,0 @@
import Vector from "./Vector"
import Trail from "./Trail"
export default class Rocket extends Trail {
constructor(childFactory, explosionFactory, position, velocity = new Vector()) {
super(childFactory, position, velocity)
this.explosionFactory = explosionFactory
this.lifetime = 10
}
update(time) {
if (this.getRemainingLifetime() && this.velocity.y > 0) {
this.explosionFactory(this)
this.lifetime = 0
}
super.update(time)
}
}

@ -1,20 +0,0 @@
export default class Time {
constructor() {
const now = Time.now()
this.delta = 0
this.elapsed = 0
this.start = now
this.previous = now
}
update() {
const now = Time.now()
this.delta = now - this.previous
this.elapsed = now - this.start
this.previous = now
}
static now() {
return Date.now() / 1000
}
}

@ -1,42 +0,0 @@
import Vector from "./Vector"
import Particle from "./Particle"
export default class Trail extends Particle {
constructor(childFactory, position, velocity = new Vector(), lifetime = 1, mass = 1) {
super(position, velocity)
this.childFactory = childFactory
this.children = []
this.lifetime = lifetime
this.mass = mass
this.isAlive = true
}
update(time) {
super.update(time)
// Add a new child on every frame
if (this.isAlive && this.getRemainingLifetime()) {
this.children.push(this.childFactory(this))
}
// Remove particles that are dead
this.children = this.children.filter(function (child) {
if (child instanceof Trail) return child.isAlive
return child.getRemainingLifetime()
})
// Kill trail if all particles fade away
if (!this.children.length) this.isAlive = false
// Update particles
this.children.forEach(child => child.update(time))
}
render(canvas, context) {
// Render all children
this.children.forEach(child => child.render(canvas, context))
}
}

@ -1,22 +0,0 @@
export default class Vector {
constructor(x = 0, y = 0) {
this.x = x
this.y = y
}
add(v) {
this.x += v.x
this.y += v.y
return this
}
multiplyScalar(s) {
this.x *= s
this.y *= s
return this
}
clone() {
return new Vector(this.x, this.y)
}
}

@ -1,141 +0,0 @@
import Vector from "./Vector"
import Time from "./Time"
import Particle from "./Particle"
import Trail from "./Trail"
import Rocket from "./Rocket"
Particle.GRAVITATION = new Vector(0, 9.81)
const getTrustParticleFactory = function () {
function getColor() {
const hue = Math.floor(Math.random() * 15 + 30)
return `hsl(${hue}, 100%, 75%`
}
return function () {
const position = this.position.clone()
const velocity = this.velocity.clone().multiplyScalar(-.1)
velocity.x += (Math.random() - .5) * 8
const color = getColor()
const radius = 1 + Math.random()
const lifetime = .5 + Math.random() * .5
const mass = .01
return new Particle(position, velocity, color, radius, lifetime, mass)
}
}
const getExplosionFactory = function (baseHue) {
function getColor() {
const hue = Math.floor(baseHue + Math.random() * 15) % 360
const lightness = Math.floor(Math.pow(Math.random(), 2) * 50 + 50)
return `hsl(${hue}, 100%, ${lightness}%`
}
function getChildFactory() {
return function (parent) {
const direction = Math.random() * Math.PI * 2
const force = 8
const velocity = new Vector(Math.cos(direction) * force, Math.sin(direction) * force)
const color = getColor()
const radius = 1 + Math.random()
const lifetime = 1
const mass = .1
return new Particle(parent.position.clone(), velocity, color, radius, lifetime, mass)
}
}
function getTrail(position) {
const direction = Math.random() * Math.PI * 2
const force = Math.random() * 128
const velocity = new Vector(Math.cos(direction) * force, Math.sin(direction) * force)
const lifetime = .5 + Math.random()
const mass = .075
return new Trail(getChildFactory(), position, velocity, lifetime, mass)
}
return function (parent) {
let trails = 32
while (trails--) {
parent.children.push(getTrail(parent.position.clone()))
}
}
}
export default class Firework {
constructor(canvas) {
this.canvas = canvas
this.ctx = canvas.getContext('2d')
this.time = new Time()
this.rockets = []
this.rAF = null
this.timer = null
this.stopSign = false
this.loop = this.loop.bind(this)
this.addRocket = this.addRocket.bind(this)
this.start()
}
start() {
this.resizeObserver = new ResizeObserver(() => this.resize())
this.resizeObserver.observe(this.canvas)
window.addEventListener('click', this.addRocket)
this.timer = window.setInterval(this.addRocket, 1000)
this.resize()
this.loop()
}
stop() {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
window.removeEventListener('click', this.addRocket)
this.stopSign = true
this.rockets = []
this.time = undefined
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
}
loop() {
if (this.stopSign) {
this.rAF && window.cancelAnimationFrame(this.rAF)
this.timer && window.clearInterval(this.timer)
return
}
this.draw()
this.rAF = window.requestAnimationFrame(this.loop)
}
draw() {
this.time.update()
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
for (let i = 0; i < this.rockets.length; i++) {
this.rockets[i].update(this.time)
this.rockets[i].render(this.canvas, this.ctx)
}
}
addRocket() {
const trustParticleFactory = getTrustParticleFactory()
const explosionFactory = getExplosionFactory(Math.random() * 360)
const position = new Vector(Math.random() * this.canvas.width, this.canvas.height)
const thrust = window.innerHeight * .75
const angle = Math.PI / -2 + (Math.random() - .5) * Math.PI / 8
const velocity = new Vector(Math.cos(angle) * thrust, Math.sin(angle) * thrust)
const lifetime = 3
this.rockets.push(new Rocket(trustParticleFactory, explosionFactory, position, velocity, lifetime))
this.rockets = this.rockets.filter(rocket => rocket.isAlive)
}
resize() {
this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight
}
}

@ -1,95 +0,0 @@
function mountainHeight(position, roughness) {
let frequencies = [1721, 947, 547, 233, 73, 31, 7]
return frequencies.reduce((height, freq) => height * roughness - Math.cos(freq * position), 0)
}
export default class Godrays {
constructor(canvas) {
this.canvas = canvas
this.ctx = canvas.getContext('2d')
this.frame = 0
this.godraysCanvas = canvas.cloneNode()
this.godraysCtx = this.godraysCanvas.getContext('2d')
this.rAF = null
this.stopSign = false
this.loop = this.loop.bind(this)
this.start()
}
start() {
this.resizeObserver = new ResizeObserver(() => this.resize())
this.resizeObserver.observe(this.canvas)
this.resize()
this.loop()
}
stop() {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
this.stopSign = true
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.godraysCtx.clearRect(0, 0, this.godraysCanvas.width, this.godraysCanvas.height)
}
loop() {
if (this.stopSign) {
this.rAF && window.cancelAnimationFrame(this.rAF)
return
}
this.draw()
this.rAF = window.requestAnimationFrame(this.loop)
}
draw() {
this.canvas.width = 512
this.canvas.height = 256
this.godraysCanvas.width = 128
this.godraysCanvas.height = 64
let sunY = Math.cos(this.frame++ / 512) * 24
let emissionGradient = this.godraysCtx.createRadialGradient(
this.godraysCanvas.width / 2, this.godraysCanvas.height / 2 + sunY, // The sun's center.
0, // Start radius.
this.godraysCanvas.width / 2, this.godraysCanvas.height / 2 + sunY, // Sun's center again.
44 // End radius.
)
this.godraysCtx.fillStyle = emissionGradient
emissionGradient.addColorStop(.1, '#0C0804') // Color for pixels in radius 0 to 4.4 (44 * .1).
emissionGradient.addColorStop(.2, '#060201') // Color for everything past radius 8.8.
this.godraysCtx.fillRect(0, 0, this.godraysCanvas.width, this.godraysCanvas.height)
this.godraysCtx.fillStyle = '#000'
let skyGradient = this.ctx.createLinearGradient(0, 0, 0, this.canvas.height)
skyGradient.addColorStop(0, '#2a3e55') // Blueish at the top.
skyGradient.addColorStop(.7, '#8d4835') // Reddish at the bottom.
this.ctx.fillStyle = skyGradient
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)
for (let i = 0; i < 4; i++) {
this.ctx.fillStyle = `hsl(7, 23%, ${23 - i * 6}%)`
for (let x = this.canvas.width; x--;) {
let mountainPosition = (this.frame + this.frame * i * i) / 3000 + x / 2000
let mountainRoughness = i / 19 - .5
let y = 128 + i * 25 + mountainHeight(mountainPosition, mountainRoughness) * 45
this.ctx.fillRect(x, y, 1, 999)
this.godraysCtx.fillRect(x / 4, y / 4 + 1, 1, 999)
}
}
this.ctx.globalCompositeOperation = this.godraysCtx.globalCompositeOperation = 'lighter'
for (let scaleFactor = 1.07; scaleFactor < 5; scaleFactor *= scaleFactor) {
this.godraysCtx.drawImage(
this.godraysCanvas,
(this.godraysCanvas.width - this.godraysCanvas.width * scaleFactor) / 2,
(this.godraysCanvas.height - this.godraysCanvas.height * scaleFactor) / 2 - sunY * scaleFactor + sunY,
this.godraysCanvas.width * scaleFactor,
this.godraysCanvas.height * scaleFactor
)
}
this.ctx.drawImage(this.godraysCanvas, 0, 0, this.canvas.width, this.canvas.height)
}
resize() {
this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight
}
}

@ -1,261 +0,0 @@
// Currently in Chrome you need to click the "Get Adobe Flash" button so it'll ask you if you want to
// allow flash to run.
// The rest of this is formatted as:
// // Explanation of the compressed code
// // ...
//
// // Commented out, compressed code
//
// Readable (more or less) version of the code
// ...
window.addEventListener("DOMContentLoaded", go)
function go() {
"use strict"
// JS1K's HTML shim gives us a canvas (a) and its 2D context (c) for free. We'll set them up here.
let canvas = document.querySelector('canvas')
let ctx = canvas.getContext('2d')
// First off - we define an abbreviation function. This takes an object, iterates over its properties
// and stores their names as strings in a 2 or 3 letter variable ("this" is the window object).
//
// p[0]+p[6] will evaluate to the 1st and 7th letter (or the 1st+"undefined" if there's no 7th),
// [p[20]] will be an empty string if the property's name is too short ([undefined] gets coerced to
// an empty string).
//
// This is a variation on Marijn Haverbeke's technique - see https://marijnhaverbeke.nl/js1k/
//
// We won't be using it in the readable version of the demo.
// A=o=>{for(p in o)this[p[0]+p[6]+[p[20]]]=p}
// Next we abbreviate all the properties in our window object because requestAnimationFrame() is
// kind of long. We can't call A(window) because it will try to abbreviate all our abbreviations (since
// it stores them in the window object) so we'll use it on "top" which has the same properties.
// We really just need a shorter requestAnimationFrame().
//
// Sidenote: this is a clear violation of JS1K rules, which is why it's very important not to read them
// before the competition is over.
// A(top)
// Now, since our demo is fairly heavy we use a small canvas, but we want it to be fullscreen on a
// black background, so we waste ~90 bytes on some CSS to stretch it (currently "object-fit:contain"
// doesn't work for canvas on MS browsers).
//
// To avoid wasting 90 bytes just on this, we take this opportunity to define P and Q as 'width' and
// 'height' for later. This is probably a mistake since I ended up packing it with regpack anyway.
//
// The weird bit at the end is an ES6 template literal being abused to call the array's join method
// with something that will be coerced into the string ':100%;'.
// a.style=[P='width',Q='height','object-fit:contain;background:#000'].join`:100%;`
canvas.style = 'width: 100%; height: 100%; object-fit:contain; background:#000;'
// Now we need a frame counter.
// t=0
let frame = 0
// B() is the requestAnimationFrame callback.
// B=_=>{
function onFrame() {
// Set width and height on our canvases, we'll be using a smaller canvas for the godrays. This
// also clears and resets their states. While we're at it, we'll store their dimensions in one
// letter vars for later.
// w=a[P]=512
// h=a[Q]=256
// W=E[P]=128
// H=E[Q]=64
canvas.width = 512
canvas.height = 256
godraysCanvas.width = 128
godraysCanvas.height = 64
// Set the sun's vertical position.
// T=C(t++/w)*24
let sunY = Math.cos(frame++ / 512) * 24 // This is actually the offset from the middle of the canvas.
// Get the 2D context for our godrays canvas, and create abbreviations for all the context properties.
// A(F=E.getContext`2d`)
let godraysCtx = godraysCanvas.getContext('2d')
// Now we set the godrays' context fillstyle (window.fy is 'fillStyle') to a newly created gradient
// (cr is 'createRadialGradient') which we also run through our abbreviator.
// A(F[fy]=g=F[cR](H,32+T,0,H,32+T,44)) // Could have shaved one more char here...
let emissionGradient = godraysCtx.createRadialGradient(
godraysCanvas.width / 2, godraysCanvas.height / 2 + sunY, // The sun's center.
0, // Start radius.
godraysCanvas.width / 2, godraysCanvas.height / 2 + sunY, // Sun's center again.
44 // End radius.
)
godraysCtx.fillStyle = emissionGradient
// Now we addColorStops. This needs to be a dark gradient because our godrays effect will basically
// overlay it on top of itself many many times, so anything lighter will result in lots of white.
//
// If you're not space-bound you can add another stop or two, maybe fade out to black, but this
// actually looks good enough.
// g[ao](.1,'#0C0804')
// g[ao](.2,'#060201')
emissionGradient.addColorStop(.1, '#0C0804') // Color for pixels in radius 0 to 4.4 (44 * .1).
emissionGradient.addColorStop(.2, '#060201') // Color for everything past radius 8.8.
// Now paint the gradient all over our godrays canvas.
// F[fc](0,0,W,H)
godraysCtx.fillRect(0, 0, godraysCanvas.width, godraysCanvas.height)
// And set the fillstyle to black, we'll use it to paint our occlusion (mountains).
// F[fy]='#000'
godraysCtx.fillStyle = '#000'
// For our 1K demo, we paint our sky a solid #644 reddish-brown. But here - let's do it right.
// c[fy]=g='#644'
// c[fc](0,0,w,h)
let skyGradient = ctx.createLinearGradient(0, 0, 0, canvas.height)
skyGradient.addColorStop(0, '#2a3e55') // Blueish at the top.
skyGradient.addColorStop(.7, '#8d4835') // Reddish at the bottom.
ctx.fillStyle = skyGradient
ctx.fillRect(0, 0, canvas.width, canvas.height)
// Our mountains will be made by summing up sine waves of varying frequencies and amplitudes.
// m=(f,j)=>[1721,947,547,233,73,31,7].reduce((a,v)=>a*j-C(f*v),0)
function mountainHeight(position, roughness) {
// Our frequencies (prime numbers to avoid extra repetitions).
let frequencies = [1721, 947, 547, 233, 73, 31, 7]
// Add them up.
return frequencies.reduce((height, freq) => height * roughness - Math.cos(freq * position), 0)
}
// Draw 4 layers of mountains.
// for(i=0;i<4;i++)for(X=w,c[fy]=`hsl(7,23%,${23-i*6}%`;X--;F[fc](X/4,U/4+1,1,w))c[fc](X,U=W+i*25+m((t+t*i*i)/1e3+X/2e3,i/19-.5)*45,1,w)
for (let i = 0; i < 4; i++) {
// Set the main canvas fillStyle to a shade of brown with variable lightness (darker at the front).
ctx.fillStyle = `hsl(7, 23%, ${23 - i * 6}%)`
// For each column in our canvas...
for (let x = canvas.width; x--;) {
// Ok, I don't really remember the details here, basically the (frame+frame*i*i) makes the
// near mountains move faster than the far ones. We divide by large numbers because our
// mountains repeat at position 1/7*Math.PI*2 or something like that...
let mountainPosition = (frame + frame * i * i) / 1000 + x / 2000
// Make further mountains more jagged, adds a bit of realism and also makes the godrays
// look nicer.
let mountainRoughness = i / 19 - .5
// 128 is the middle, i * 25 moves the nearer mountains lower on the screen.
let y = 128 + i * 25 + mountainHeight(mountainPosition, mountainRoughness) * 45
// Paint a 1px-wide rectangle from the mountain's top to below the bottom of the canvas.
ctx.fillRect(x, y, 1, 999) // 999 can be any large number...
// Paint the same thing in black on the godrays emission canvas, which is 1/4 the size,
// and move it one pixel down (otherwise there can be a tiny underlit space between the
// mountains and the sky).
godraysCtx.fillRect(x / 4, y / 4 + 1, 1, 999)
}
}
// The godrays are generated by adding up RGB values, gCt is the bane of all js golfers -
// globalCompositeOperation. Set it to 'lighter' on both canvases.
// c[gCt]=F[gCt]='lighter'
ctx.globalCompositeOperation = godraysCtx.globalCompositeOperation = 'lighter'
// NOW - let's light this motherfucker up! We'll make several passes over our emission canvas,
// each time adding an enlarged copy of it to itself so at the first pass we get 2 copies, then 4,
// then 8, then 16 etc... We square our scale factor at each iteration.
// for(s=1.07;s<5;s*=s)F[da](E,(W-W*s)/2,(H-H*s)/2-T*s+T,W*s,H*s)
for (let scaleFactor = 1.07; scaleFactor < 5; scaleFactor *= scaleFactor) {
// The x, y, width and height arguments for drawImage keep the light source (godraysCanvas.width
// / 2, godraysCanvas.height / 2 + sunY) in the same spot on the enlarged copy. It basically boils
// down to multiplying a 2D matrix by itself. There's probably a better way to do this, but I
// couldn't figure it out.
godraysCtx.drawImage(
godraysCanvas,
(godraysCanvas.width - godraysCanvas.width * scaleFactor) / 2,
(godraysCanvas.height - godraysCanvas.height * scaleFactor) / 2 - sunY * scaleFactor + sunY,
godraysCanvas.width * scaleFactor,
godraysCanvas.height * scaleFactor
)
}
// Now that our godrays are rendered, draw them to our output canvas (whose globalCompositeOperation
// is already set to 'lighter').
// c[da](E,0,0,w,h)
ctx.drawImage(godraysCanvas, 0, 0, canvas.width, canvas.height)
// All done.
// this[rte](B)}
window.requestAnimationFrame(onFrame)
}
// Call our requestAnimationFrame handler to start rendering. Since it takes no arguments use the argument
// list to create our godrays canvas with cloneNode, which also takes no arguments... use it to setup a
// Math.cos shortcut (we'll skip this in our longform version).
// B(E=a.cloneNode(C=Math.cos))
let godraysCanvas = canvas.cloneNode()
onFrame()
// Phew... that took a while, but we're finally done with the visuals. Now for the audio part -
//
// The synthesizer is based on the Karplus-Strong algorithm which uses a very short delay loop as a
// resonator. I was initially aiming for a realistic string quartet but time and space constraints
// have forced me to massively compromise.
//
//
// The music is a 64-note melody that ends up an octave above where it started, spread out in a 4-voice
// canon. We pre-render a single voice and then add up 4 in our ScriptProcessor callback.
// Big hairy render loop, let's break it to pieces and explain...
// for(M=[Y=[V=J=I=i=0]];i<h;i++)for(j=2e4;j--;T=Y[I|0]=M[J++]=O%9)O=Math.random()-.5+T/5+Y[(I=++I%(7e3/2**(("!!----,*,(444420/20/-0/---,,--//((4444202/;;;;986986420/00--//,,".charCodeAt(i&63)+12*(i>>6))/12)))|0]*.8||0
let encodedMelody = "!!----,*,(444420/20/-0/---,,--//((4444202/;;;;986986420/00--//,,"
// M=[Y=[V=J=I=i=0]]
let voiceBuffer = [] // M = [...]
let ksDelayBuffer = [] // Y = [...]
let sampleOffset = 0 // V = 0 (used later)
let J = 0 // What the hell is J????
// Oh fuck it. It's 4am and I have no idea how this thing works. Maybe I'll write it up later.
// Besides, you just came here for the godrays, right?
// A(G=new AudioContext)
// A(S=G[cSr](w*8,0,1))
// S[oo]=e=>{A(e);A(o=e[oB]);for(i=0;i<w*8;o[gn](0)[i++]=O/32,V++)for(O=0,K=4;K--;O+=T>0&&M[T%J])T=V-(K/32*9)*J}
// S.connect(G[da])
}

@ -1,30 +0,0 @@
export default class Particle {
constructor() {
this.angleX = Math.random() * Math.PI * 2
this.angleY = Math.random() * Math.PI * 2
this.speedX = Math.random()
this.speedY = Math.random()
this.radius = 500
}
update({ctx, width, height, gravityDistance, mousePos}, speed = 0.035) {
let x = Math.cos(this.angleX) * this.radius,
y = Math.sin(this.angleY) * this.radius,
currentSpeedX = this.speedX * speed,
currentSpeedY = this.speedY * speed
if (Math.abs(mousePos.x - width / 2 - x) < gravityDistance && Math.abs(mousePos.y - height / 2 - y) < gravityDistance) {
currentSpeedX = currentSpeedX * (Math.abs(mousePos.x - width / 2 - x) / gravityDistance) * 0.5
currentSpeedY = currentSpeedY * (Math.abs(mousePos.y - height / 2 - y) / gravityDistance) * 0.5
}
this.angleX += currentSpeedX
this.angleY += currentSpeedY
ctx.beginPath()
ctx.fillStyle = "white"
ctx.arc(width / 2 + x, height / 2 + y, 2.5, 0, Math.PI * 2, false)
ctx.fill()
}
}

@ -1,71 +0,0 @@
import Particle from "./Particle"
export default class MoveFollowMouse {
constructor(canvas, {num = 10, gravityDistance = 100} = {}) {
this.canvas = canvas
this.ctx = canvas.getContext('2d')
this.rAF = null
this.stopSign = false
this.particles = []
this.num = num
this.gravityDistance = gravityDistance
this.mousePos = {x: 0, y: 0}
this.loop = this.loop.bind(this)
this.getMousePos = this.getMousePos.bind(this)
this.start()
}
start() {
for (let i = 0; i < this.num; i += 1) {
this.particles.push(new Particle())
}
this.resizeObserver = new ResizeObserver(() => this.resize())
this.resizeObserver.observe(this.canvas)
window.addEventListener('mousemove', this.getMousePos)
this.resize()
this.loop()
}
stop() {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
window.removeEventListener('mousemove', this.getMousePos)
this.stopSign = true
this.particles = []
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
}
loop() {
if (this.stopSign) {
this.rAF && window.cancelAnimationFrame(this.rAF)
return
}
this.draw()
this.rAF = window.requestAnimationFrame(this.loop)
}
draw() {
this.ctx.clearRect(0, 0, this.width, this.height)
for (let i = 0; i < this.num; i += 1) {
this.particles[i].update({
ctx: this.ctx,
width: this.width,
height: this.height,
gravityDistance: this.gravityDistance,
mousePos: this.mousePos
})
}
}
getMousePos(evt) {
const rect = this.canvas.getBoundingClientRect()
this.mousePos = {x: evt.clientX - rect.left, y: evt.clientY - rect.top}
}
resize() {
this.width = this.canvas.width = window.innerWidth
this.height = this.canvas.height = window.innerHeight
}
}

@ -1,90 +0,0 @@
import Vector from "./Vector"
import util from "./util"
export default class Particle {
constructor({add, sphereRad, fLen, dotImageList}) {
this.distPos = {
theta: add === true ? Math.PI / 2 : Math.random() * Math.PI,
phi: 2 * Math.random() * Math.PI
}
this.distPos.x = sphereRad * Math.sin(this.distPos.theta) * Math.cos(this.distPos.phi)
this.distPos.y = sphereRad * Math.sin(this.distPos.theta) * Math.sin(this.distPos.phi)
this.distPos.z = sphereRad * Math.cos(this.distPos.theta)
this.distVec = new Vector(this.distPos.x, this.distPos.y, this.distPos.z)
this.unitVec = this.distVec.unit()
this.startVec = this.distVec.multiply(1 + Math.random() * 2)
this.x = this.startVec.x
this.y = this.startVec.y
this.z = this.startVec.z
this.veloRate = 1 + Math.random()
this.velo = this.unitVec.negative().multiply(this.veloRate)
this.finalVelo = 0
this.m = fLen / (fLen - this.z)
this.age = this.life = 50 + Math.floor(Math.random() * 500)
this.turnAngle = 0
this.wanderTime = 200
this.radius = 1 + Math.random() * 3
let colorRandom = Math.floor(Math.random() * 4)
switch (colorRandom) {
case 0:
this.img = dotImageList[0]
this.color = "rgba(70,255,140,"
break
case 1:
this.img = dotImageList[1]
this.color = "rgba(90,90,90,"
break
case 2:
this.img = dotImageList[2]
break
case 3:
this.img = dotImageList[3]
break
}
}
update({sphereRad, fLen, turnSpeed}) {
this.nowPos = new Vector(this.x, this.y, this.z)
this.nowPosUnit = this.nowPos.unit()
if (this.wanderTime > 0 && this.nowPos.length() > (sphereRad * 1.2)) {
this.wanderTime--
this.velo.x += 0.1 * (Math.random() * 2 - 1)
this.velo.y += 0.1 * (Math.random() * 2 - 1)
this.velo.z += 0.1 * (Math.random() * 2 - 1)
this.x = this.x + this.velo.x
this.y = this.y + this.velo.y
this.z = this.z + this.velo.z
this.op = util.map(this.nowPos.length(), sphereRad, this.startVec.length(), 1, 0)
}
else if (this.nowPos.length() > sphereRad) {
if (this.finalPos === 0) {
this.finalPos = this.nowPosUnit.multiply(sphereRad)
}
if (this.finalVelo === 0) {
this.finalVelo = this.nowPosUnit.negative().multiply(this.veloRate)
}
this.x = this.x + this.finalVelo.x
this.y = this.y + this.finalVelo.y
this.z = this.z + this.finalVelo.z
this.op = util.map(this.nowPos.length(), sphereRad, this.startVec.length(), 1, 0)
}
else {
this.op = this.life / (this.age / 2)
this.turnAngle = (this.turnAngle + turnSpeed) % (Math.PI * 2)
let cosAngle = Math.cos(turnSpeed)
let sinAngle = Math.sin(turnSpeed)
this.x = cosAngle * this.nowPos.x + sinAngle * this.nowPos.z
this.z = -sinAngle * this.nowPos.x + cosAngle * this.nowPos.z
this.y = this.nowPos.y
this.life--
}
this.m = fLen / (fLen - this.z)
}
}

@ -1,74 +0,0 @@
export default class Vector {
constructor(x, y, z) {
this.x = x || 0
this.y = y || 0
this.z = z || 0
}
negative() {
return new Vector(-this.x, -this.y, -this.z)
}
add(v) {
if (v instanceof Vector) return new Vector(this.x + v.x, this.y + v.y, this.z + v.z)
else return new Vector(this.x + v, this.y + v, this.z + v)
}
subtract(v) {
if (v instanceof Vector) return new Vector(this.x - v.x, this.y - v.y, this.z - v.z)
else return new Vector(this.x - v, this.y - v, this.z - v)
}
multiply(v) {
if (v instanceof Vector) return new Vector(this.x * v.x, this.y * v.y, this.z * v.z)
else return new Vector(this.x * v, this.y * v, this.z * v)
}
divide(v) {
if (v instanceof Vector) return new Vector(this.x / v.x, this.y / v.y, this.z / v.z)
else return new Vector(this.x / v, this.y / v, this.z / v)
}
dot(v) {
return this.x * v.x + this.y * v.y + this.z * v.z
}
cross(v) {
return new Vector(
this.y * v.z - this.z * v.y,
this.z * v.x - this.x * v.z,
this.x * v.y - this.y * v.x
)
}
length() {
return Math.sqrt(this.dot(this))
}
unit() {
return this.divide(this.length())
}
min() {
return Math.min(Math.min(this.x, this.y), this.z)
}
max() {
return Math.max(Math.max(this.x, this.y), this.z)
}
angleTo(a) {
return Math.acos(this.dot(a) / (this.length() * a.length()))
}
clone() {
return new Vector(this.x, this.y, this.z)
}
init(x, y, z) {
this.x = x
this.y = y
this.z = z
return this
}
}

@ -1,91 +0,0 @@
import Particle from "./Particle"
export default class ParticleBall {
constructor(canvas, {sphereRad = 130, fLen = 300, maxParticle = 400, turnSpeed = 0.005} = {}) {
this.canvas = canvas
this.ctx = canvas.getContext("2d")
this.rAF = null
this.stopSign = false
this.sphereRad = sphereRad
this.fLen = fLen
this.maxParticle = maxParticle
this.turnSpeed = turnSpeed
this.particles = []
this.loop = this.loop.bind(this)
this.start()
}
start() {
this.resizeObserver = new ResizeObserver(() => this.resize())
this.resizeObserver.observe(this.canvas)
this.resize()
this.initDot()
this.loop()
}
stop() {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
this.stopSign = true
this.particles = []
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
}
loop() {
if (this.stopSign) {
this.rAF && window.cancelAnimationFrame(this.rAF)
return
}
this.draw()
this.rAF = window.requestAnimationFrame(this.loop)
}
draw() {
this.ctx.clearRect(0, 0, this.width, this.height)
if (this.particles.length < this.maxParticle) {
this.particles.push(new Particle({
sphereRad: this.sphereRad,
fLen: this.fLen,
dotImageList: this.dotImageList
}))
}
for (let i = 0; i < this.particles.length; i++) {
let p = this.particles[i]
if (p.life === 0) {
this.particles.splice(i, 1)
this.particles.push(new Particle({
sphereRad: this.sphereRad,
fLen: this.fLen,
dotImageList: this.dotImageList
}))
}
p.update({sphereRad: this.sphereRad, fLen: this.fLen, turnSpeed: this.turnSpeed})
if (p.m > 0) {
this.ctx.save()
this.ctx.globalAlpha = p.op
this.ctx.drawImage(p.img, p.x * p.m + this.CenterX, this.CenterY - p.y * p.m, p.radius * p.m * 2, p.radius * p.m * 2)
this.ctx.restore()
}
}
}
resize() {
this.width = this.canvas.width = window.innerWidth
this.height = this.canvas.height = window.innerHeight
this.CenterX = this.width / 2
this.CenterY = this.height / 2
}
initDot() {
this.dotImageList = []
for (let i = 1; i <= 4; i++) {
const dotImage = new Image()
dotImage.src = `${process.env.BASE_URL}static/img/dot${i}.png`
this.dotImageList.push(dotImage)
}
}
}

@ -1,13 +0,0 @@
const util = {
norm: function (value, min, max) {
return (value - min) / (max - min)
},
lerp: function (norm, min, max) {
return min + norm * (max - min)
},
map: function (value, sourceMin, sourceMax, destMin, destMax) {
return this.lerp(this.norm(value, sourceMin, sourceMax), destMin, destMax)
}
}
export default util

@ -1,50 +0,0 @@
function getLimitedRandom(min, max, roundToInteger) {
let number = Math.random() * (max - min) + min
if (roundToInteger) number = Math.round(number)
return number
}
function returnRandomArrayItem(array) {
return array[Math.floor(Math.random() * array.length)]
}
export default class Particle {
constructor(parent, x, y) {
this.network = parent
this.canvas = parent.canvas
this.ctx = parent.ctx
this.particleColor = returnRandomArrayItem(this.network.options.particleColors)
this.radius = getLimitedRandom(1.5, 2.5)
this.opacity = 0
this.x = x || Math.random() * this.canvas.width
this.y = y || Math.random() * this.canvas.height
this.velocity = {
x: (Math.random() - 0.5) * parent.options.velocity,
y: (Math.random() - 0.5) * parent.options.velocity
}
}
update() {
this.opacity = this.opacity < 1 ? this.opacity + 0.01 : 1
// Change dir if outside map
if (this.x > this.canvas.width + 100 || this.x < -100) {
this.velocity.x = -this.velocity.x
}
if (this.y > this.canvas.height + 100 || this.y < -100) {
this.velocity.y = -this.velocity.y
}
// Update position
this.x += this.velocity.x
this.y += this.velocity.y
}
draw() {
this.ctx.beginPath()
this.ctx.fillStyle = this.particleColor
this.ctx.globalAlpha = this.opacity
this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI)
this.ctx.fill()
}
}

@ -1,207 +0,0 @@
import Particle from "./Particle"
export default class ParticleNetwork {
constructor(canvas) {
this.canvas = canvas
this.ctx = canvas.getContext("2d")
this.options = {
velocity: 1, // the higher the faster
density: 15000, // the lower the denser
netLineDistance: 200,
netLineColor: '#929292',
particleColors: ['#aaa'] // ['#6D4E5C', '#aaa', '#FFC458' ]
}
this.rAF = null
this.timer = null
this.stopSign = false
this.loop = this.loop.bind(this)
this.start()
}
start() {
this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight
this.createParticles(true)
this.bindUiActions()
this.loop()
}
stop() {
this.unbindUiActions()
this.stopSign = true
this.particles = []
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
}
loop() {
if (this.stopSign) {
this.rAF && window.cancelAnimationFrame(this.rAF)
return
}
this.draw()
this.rAF = window.requestAnimationFrame(this.loop)
}
draw() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.ctx.globalAlpha = 1
// Draw connections
for (let i = 0; i < this.particles.length; i++) {
for (let j = this.particles.length - 1; j > i; j--) {
let distance, p1 = this.particles[i], p2 = this.particles[j]
// check very simply if the two points are even a candidate for further measurements
distance = Math.min(Math.abs(p1.x - p2.x), Math.abs(p1.y - p2.y))
if (distance > this.options.netLineDistance) continue
// the two points seem close enough, now let's measure precisely
distance = Math.sqrt(
Math.pow(p1.x - p2.x, 2) +
Math.pow(p1.y - p2.y, 2)
)
if (distance > this.options.netLineDistance) continue
this.ctx.beginPath()
this.ctx.strokeStyle = this.options.netLineColor
this.ctx.globalAlpha = (this.options.netLineDistance - distance) / this.options.netLineDistance * p1.opacity * p2.opacity
this.ctx.lineWidth = 0.7
this.ctx.moveTo(p1.x, p1.y)
this.ctx.lineTo(p2.x, p2.y)
this.ctx.stroke()
}
}
// Draw particles
for (let i = 0; i < this.particles.length; i++) {
this.particles[i].update()
this.particles[i].draw()
}
}
createParticles(isInitial) {
// Initialise / reset particles
this.particles = []
let quantity = this.canvas.width * this.canvas.height / this.options.density
if (isInitial) {
let counter = 0
window.clearInterval(this.timer)
this.timer = window.setInterval(() => {
if (counter < quantity - 1) this.particles.push(new Particle(this))
else window.clearInterval(this.timer)
counter++
}, 250)
}
else for (let i = 0; i < quantity; i++) this.particles.push(new Particle(this))
}
createInteractionParticle() {
this.interactionParticle = new Particle(this)
this.interactionParticle.velocity = {x: 0, y: 0}
this.particles.push(this.interactionParticle)
return this.interactionParticle
}
removeInteractionParticle() {
let index = this.particles.indexOf(this.interactionParticle)
if (index > -1) {
this.interactionParticle = undefined
this.particles.splice(index, 1)
}
}
bindUiActions() {
this.spawnQuantity = 3
this.mouseIsDown = false
this.touchIsMoving = false
this.onMouseMove = function (e) {
!this.interactionParticle && this.createInteractionParticle()
this.interactionParticle.x = e.offsetX
this.interactionParticle.y = e.offsetY
}.bind(this)
this.onTouchMove = function (e) {
e.preventDefault()
this.touchIsMoving = true
!this.interactionParticle && this.createInteractionParticle()
this.interactionParticle.x = e.changedTouches[0].clientX
this.interactionParticle.y = e.changedTouches[0].clientY
}.bind(this)
this.onMouseDown = function (e) {
this.mouseIsDown = true
let counter = 0
let quantity = this.spawnQuantity
let intervalId = window.setInterval(function () {
if (this.mouseIsDown) {
if (counter === 1) quantity = 1
for (let i = 0; i < quantity; i++) {
if (this.interactionParticle) {
this.particles.push(new Particle(this, this.interactionParticle.x, this.interactionParticle.y))
}
}
}
else window.clearInterval(intervalId)
counter++
}.bind(this), 50)
}.bind(this)
this.onTouchStart = function (e) {
e.preventDefault()
window.setTimeout(function () {
if (!this.touchIsMoving) {
for (let i = 0; i < this.spawnQuantity; i++) {
this.particles.push(new Particle(this, e.changedTouches[0].clientX, e.changedTouches[0].clientY))
}
}
}.bind(this), 200)
}.bind(this)
this.onMouseUp = function (e) {
this.mouseIsDown = false
}.bind(this)
this.onMouseOut = function (e) {
this.removeInteractionParticle()
}.bind(this)
this.onTouchEnd = function (e) {
e.preventDefault()
this.touchIsMoving = false
this.removeInteractionParticle()
}.bind(this)
this.resizeObserver = new ResizeObserver(() => this.resize())
this.resizeObserver.observe(this.canvas)
window.addEventListener('mousemove', this.onMouseMove)
window.addEventListener('touchmove', this.onTouchMove)
window.addEventListener('mousedown', this.onMouseDown)
window.addEventListener('touchstart', this.onTouchStart)
window.addEventListener('mouseup', this.onMouseUp)
window.addEventListener('mouseout', this.onMouseOut)
window.addEventListener('touchend', this.onTouchEnd)
}
unbindUiActions() {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
window.removeEventListener('mousemove', this.onMouseMove)
window.removeEventListener('touchmove', this.onTouchMove)
window.removeEventListener('mousedown', this.onMouseDown)
window.removeEventListener('touchstart', this.onTouchStart)
window.removeEventListener('mouseup', this.onMouseUp)
window.removeEventListener('mouseout', this.onMouseOut)
window.removeEventListener('touchend', this.onTouchEnd)
}
resize() {
this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.createParticles()
}
}

@ -1,160 +0,0 @@
function colorIntToHexString(color) {
let s = color.toString(16)
return '0'.repeat(6 - s.length) + s
}
export default class ParticleWave {
constructor(canvas) {
this.canvas = canvas
this.ctx = canvas.getContext("2d")
this.config = {
colors: {
background: 0x000000,
particle: 0x477cc2
},
alpha: {
particle: 1
},
particleCount: 10000
}
this.particleWaveWalker = 0
this.rAF = null
this.stopSign = false
this.loop = this.loop.bind(this)
this.start()
}
initParticle() {
this.particles = new Float32Array(this.config.particleCount * 2)
for (let i = 0; i < this.particles.length; i += 2) {
this.particles[i] = Math.random()
this.particles[i + 1] = Math.random()
}
}
initParticleColor() {
this.particleColorRGB = new Float32Array(3)
this.particleColorRGB[0] = this.config.colors.particle >> 16 & 0xff
this.particleColorRGB[1] = this.config.colors.particle >> 8 & 0xff
this.particleColorRGB[2] = this.config.colors.particle & 0xff
this.particleFillStyle = 'rgb(' + this.particleColorRGB[0] + ',' + this.particleColorRGB[1] + ',' + this.particleColorRGB[2] + ')'
}
initSmoothGradient() {
this.smoothGradient = this.ctx.createLinearGradient(
this.canvas.width / 2,
0,
this.canvas.width / 2,
this.canvas.height
)
this.smoothGradient.addColorStop(0.25, 'rgba(0, 0, 0, 0)')
this.smoothGradient.addColorStop(0.45, 'rgba(0, 0, 0, 0.9)')
this.smoothGradient.addColorStop(0.5, 'rgba(0, 0, 0, 1)')
this.smoothGradient.addColorStop(0.55, 'rgba(0, 0, 0, 0.9)')
this.smoothGradient.addColorStop(0.75, 'rgba(0, 0, 0, 0)')
}
initWaterGradient() {
this.waterGradient = this.ctx.createLinearGradient(
this.canvas.width / 2,
this.canvas.height / 2,
this.canvas.width / 2,
this.canvas.height
)
this.waterGradient.addColorStop(0, 'rgba(0, 0, 30, 0)')
this.waterGradient.addColorStop(1, 'rgba(30, 0, 60, 0.5)')
}
start() {
this.resizeObserver = new ResizeObserver(() => this.resize())
this.resizeObserver.observe(this.canvas)
this.resize()
this.initParticle()
this.initParticleColor()
this.initSmoothGradient()
this.initWaterGradient()
this.loop()
}
stop() {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
this.stopSign = true
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
}
loop() {
if (this.stopSign) {
this.rAF && window.cancelAnimationFrame(this.rAF)
return
}
this.draw()
this.rAF = window.requestAnimationFrame(this.loop)
}
draw() {
this.ctx.fillStyle = '#' + colorIntToHexString(this.config.colors.background)
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)
this.ctx.fillStyle = this.waterGradient
this.ctx.fillRect(0, this.canvas.height / 2, this.canvas.width, this.canvas.height / 2)
this.renderParticle()
this.ctx.fillStyle = this.particleFillStyle
this.ctx.fillStyle = this.smoothGradient
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)
}
renderParticle() {
this.particleWaveWalker += 0.03
this.ctx.fillStyle = this.particleFillStyle
let radius = {min: 1, add: 5},
midY = this.canvas.height / 2,
midX = this.canvas.width / 2,
spreadX = 5,
spreadZ = 0.0,
modZ = 0.0,
addX = 0,
addY = 0,
p = {x: 0.0, y: 0.0, r: 0.0},
waveControl = 10
for (let i = 0, xIndex, zIndex; i < this.particles.length; i += 2) {
xIndex = i
zIndex = i + 1
this.particles[zIndex] += 0.003
if (this.particles[zIndex] > 1) {
this.particles[zIndex] = 0
this.particles[xIndex] = Math.random()
}
if (this.particles[zIndex] < 0.3) continue
modZ = Math.pow(this.particles[zIndex], 2)
spreadZ = 1 + (spreadX - 1) * modZ
addX = (0.5 - this.particles[xIndex]) * this.canvas.width * spreadZ
addY = midY * modZ * (1 + 3 / waveControl)
p.x = midX + addX
p.y = midY + addY
p.r = radius.min + modZ * radius.add
p.y += Math.sin(this.particles[xIndex] * 50 + this.particleWaveWalker) * addY / waveControl
p.y += Math.cos(this.particles[zIndex] * 10 + this.particleWaveWalker) * addY / waveControl
p.y -= Math.cos(this.particles[zIndex] + this.particles[xIndex] * 10 + this.particleWaveWalker) * addY / waveControl
p.y -= Math.cos(this.particles[xIndex] * 50 + this.particleWaveWalker) * addY / waveControl
p.y -= Math.sin(this.particles[zIndex] * 10 + this.particleWaveWalker) * addY / waveControl
if (p.x < 0 || p.x > this.canvas.width) continue
this.ctx.fillRect(p.x, p.y, p.r, p.r)
}
}
resize() {
this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight
}
}

@ -1,29 +0,0 @@
import Vector from "./Vector"
export default class RainDrop {
constructor(parent) {
this.size = 2
this.parent = parent
this.init()
}
init() {
this.life = 0
this.ttl = Math.random() * 500 + 300
this.position = new Vector(Math.random() * window.innerWidth, 0)
this.velocity = new Vector(0.5 - Math.random(), Math.random() + 0.2)
}
update() {
if (this.position.x > window.innerWidth || this.position.x < -this.size || this.life > this.ttl) {
this.init()
}
if (this.position.y > this.parent.floor) {
this.position.y = this.parent.floor - this.size
this.velocity.y *= -0.2 - Math.random() * 0.3
}
this.life++
this.position.add(this.velocity)
this.velocity.y += 0.1
}
}

@ -1,11 +0,0 @@
export default class Vector {
constructor(x, y) {
this.x = x || 0
this.y = y || 0
}
add({x, y}) {
this.x += x
this.y += y
}
}

@ -1,85 +0,0 @@
import RainDrop from "./RainDrop"
export default class ReflectRain {
constructor(canvas, {rainDropCount = 500, rainColor = 'rgba(150,180,255,0.8)', backgroundColor = '#2d3a4b'} = {}) {
this.props = {rainDropCount, rainColor, backgroundColor}
this.rainDrops = []
this.timer = null
this.rAF = null
this.stopSign = false
this.canvas = canvas
this.ctx = canvas.getContext('2d')
this.loop = this.loop.bind(this)
this.start()
}
start() {
this.resizeObserver = new ResizeObserver(() => this.resize())
this.resizeObserver.observe(this.canvas)
this.resize()
this.loop()
}
stop() {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
this.stopSign = true
this.rainDrops = []
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
}
loop() {
if (this.stopSign) {
this.rAF && window.cancelAnimationFrame(this.rAF)
this.timer && window.clearTimeout(this.timer)
return
}
if (this.rainDrops.length < this.props.rainDropCount) {
this.timer = window.setTimeout(() => this.rainDrops.push(new RainDrop(this)), Math.random() * 200)
}
else if (this.timer) {
window.clearTimeout(this.timer)
this.timer = null
}
this.draw()
this.rAF = window.requestAnimationFrame(this.loop)
}
draw() {
this.ctx.fillStyle = this.props.backgroundColor
this.ctx.fillRect(0, 0, this.dimensions.width, this.floor)
for (let i = 0, len = this.rainDrops.length; i < len; i++) {
let rainDrop = this.rainDrops[i]
rainDrop.update()
this.ctx.fillStyle = this.props.rainColor
this.ctx.fillRect(rainDrop.position.x, rainDrop.position.y, rainDrop.size, rainDrop.size)
}
this.reflect()
}
reflect() {
let grad = this.ctx.createLinearGradient(this.dimensions.width / 2, this.floor * 0.6, this.dimensions.width / 2, this.floor)
grad.addColorStop(0, 'rgba(80,90,100,1)')
grad.addColorStop(1, 'rgba(80,90,100,0)')
this.ctx.save()
this.ctx.scale(1, -1)
this.ctx.translate(0, this.floor * -2)
this.ctx.filter = 'blur(3px) saturate(150%)'
this.ctx.drawImage(this.canvas, 0, 0, this.dimensions.width, this.floor, 0, 0, this.dimensions.width, this.floor)
this.ctx.fillStyle = grad
this.ctx.fillRect(0, 0, this.dimensions.width, this.floor)
this.ctx.restore()
}
resize() {
this.dimensions = {
width: window.innerWidth,
height: window.innerHeight
}
this.canvas.width = this.dimensions.width
this.canvas.height = this.dimensions.height
this.floor = this.dimensions.height * 0.7
}
}

@ -1,18 +0,0 @@
import {rand} from "./util"
export default class Spark {
constructor(x, y, options) {
this.x = x
this.y = y
this.age = 0
this.acceleration = rand(options.acceleration[0], options.acceleration[1])
this.color = options.randColor ? rand(0, 255) + "," + rand(0, 255) + "," + rand(0, 255) : OPT.color
this.opacity = options.maxOpacity - this.age / (options.lifetime * rand(1, 10))
}
go(options) {
this.x += options.speed * options.direction.x * this.acceleration / 2
this.y += options.speed * options.direction.y * this.acceleration / 2
this.opacity = options.maxOpacity - ++this.age / options.lifetime
}
}

@ -1,89 +0,0 @@
import Spark from "./Spark"
import {rand} from "./util"
export default class SparkRain {
constructor(canvas) {
this.canvas = canvas
this.ctx = canvas.getContext("2d")
this.options = {
amount: 10000,
speed: 0.1, // pixels per frame
lifetime: 500,
direction: {x: -0.5, y: 1},
size: [1, 1],
maxOpacity: 1,
color: "150, 150, 150",
randColor: true,
acceleration: [5, 40]
}
if (window.innerWidth < 520) {
this.options.speed = 0.05
this.options.color = "150, 150, 150"
}
this.sparks = []
this.rAF = null
this.timer = null
this.stopSign = false
this.loop = this.loop.bind(this)
this.addSpark = this.addSpark.bind(this)
this.start()
}
start() {
this.resizeObserver = new ResizeObserver(() => this.resize())
this.resizeObserver.observe(this.canvas)
this.resize()
this.timer = window.setInterval(
() => this.sparks.length < this.options.amount && this.addSpark(),
1000 / this.options.amount)
this.loop()
}
stop() {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
this.stopSign = true
this.sparks = []
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
}
loop() {
if (this.stopSign) {
this.rAF && window.cancelAnimationFrame(this.rAF)
this.timer && window.clearInterval(this.timer)
return
}
this.draw()
this.rAF = window.requestAnimationFrame(this.loop)
}
draw() {
this.ctx.fillStyle = 'rgba(0,0,0, 0.1)'
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)
for (let i = 0; i < this.sparks.length; i++) {
this.sparks[i].opacity <= 0 ? this.sparks.splice(i, 1) : this.drawSpark(this.sparks[i])
}
}
addSpark() {
let x = rand(-200, this.canvas.width + 200)
let y = rand(-200, this.canvas.height + 200)
this.sparks.push(new Spark(x, y, this.options))
}
drawSpark(spark) {
spark.go(this.options)
this.ctx.beginPath()
this.ctx.fillStyle = `rgba(${spark.color},${spark.opacity})`
this.ctx.rect(spark.x, spark.y, this.options.size[0], this.options.size[1])
this.ctx.fill()
}
resize() {
this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight
}
}

@ -1,3 +0,0 @@
export function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}

@ -1,81 +0,0 @@
function rand(min, max) {
return Math.random() * (max - min) + min
}
export default class Particle {
constructor() {
this.reset()
}
reset() {
this.x = 0
this.y = 0
this.z = 0
this.vx = rand(-0.5, 0.5)
this.vy = rand(-0.5, 0.5)
this.vz = rand(-0.25, 0.5)
this.s = 0
this.sx = 0
this.sy = 0
this.life = 1
this.decay = rand(0.005, 0.02)
this.radius = rand(5, 15)
this.sradius = this.radius
this.rradius = this.radius
this.hue = 0
this.alpha = 1
this.angle = 0
}
step($) {
this.vx *= $.mouse.down ? 1.1 : 1.04
this.vy *= $.mouse.down ? 1.1 : 1.04
this.vz *= $.mouse.down ? 1.1 : 1.04
this.x += this.vx
this.y += this.vy
this.z += this.vz
this.s = $.fl / ($.fl + this.z)
this.sx = this.x * this.s
this.sy = this.y * this.s
this.sradius = this.radius * this.s
this.rradius = Math.max(0.0001, this.sradius * this.life)
this.angle = Math.atan2(this.sy, this.sx)
this.hue = (this.angle / (Math.PI * 2)) * 180 + $.tick * 4
this.alpha = this.life
if (this.z < $.bounds.z.min) return this.reset()
if (this.life > 0) this.life -= this.decay
else this.reset()
}
draw($) {
$.ctx.beginPath()
$.ctx.arc(this.sx, this.sy, this.rradius * 2, 0, Math.PI * 2)
$.ctx.fillStyle = 'hsla(' + (this.hue + 60) + ', 60%, 30%, ' + this.alpha / 3 + ')'
$.ctx.fill()
$.ctx.strokeStyle = 'hsla(' + (this.hue - 60) + ', 60%, 30%, ' + this.alpha / 2 + ')'
$.ctx.stroke()
let angle1 = this.angle + Math.PI / 2,
angle2 = this.angle,
angle3 = this.angle - Math.PI / 2
$.ctx.beginPath()
$.ctx.moveTo(0, 0)
$.ctx.lineTo(this.sx + Math.cos(angle1) * this.rradius, this.sy + Math.sin(angle1) * this.rradius)
$.ctx.lineTo(this.sx + Math.cos(angle2) * this.rradius * 6, this.sy + Math.sin(angle2) * this.rradius * 6)
$.ctx.lineTo(this.sx + Math.cos(angle3) * this.rradius, this.sy + Math.sin(angle3) * this.rradius)
$.ctx.closePath()
$.ctx.fillStyle = 'hsla(' + this.hue + ', 50%, 30%, ' + this.alpha / 2 + ')'
$.ctx.fill()
$.ctx.beginPath()
$.ctx.moveTo(this.sx + Math.cos(angle2) * this.rradius * 6, this.sy + Math.sin(angle2) * this.rradius * 6)
$.ctx.lineTo(0, 0)
$.ctx.strokeStyle = 'hsla(' + this.hue + ', 50%, 30%, ' + this.alpha + ')'
$.ctx.stroke()
let sparkleRadius = this.rradius * 4
$.ctx.fillStyle = 'hsla(' + (this.hue + 180) + ', 100%, 50%, ' + this.alpha * 2 + ')'
$.ctx.fillRect((this.sx + rand(-sparkleRadius, sparkleRadius)), (this.sy + rand(-sparkleRadius, sparkleRadius)), 1, 1)
}
}

@ -1,97 +0,0 @@
import Particle from "./Particle"
export default class Sunlight {
constructor(canvas) {
this.canvas = canvas
this.ctx = canvas.getContext('2d')
this.parts = []
this.mouse = {down: 0}
this.rAF = null
this.stopSign = false
this.loop = this.loop.bind(this)
this.mousedown = this.mousedown.bind(this)
this.mouseup = this.mouseup.bind(this)
this.start()
}
start() {
this.resizeObserver = new ResizeObserver(() => this.resize())
this.resizeObserver.observe(this.canvas)
window.addEventListener('mouseup', this.mouseup)
window.addEventListener('mousedown', this.mousedown)
this.resize()
this.loop()
}
stop() {
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
window.removeEventListener('mouseup', this.mouseup)
window.removeEventListener('mousedown', this.mousedown)
this.stopSign = true
this.parts = []
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
}
loop() {
if (this.stopSign) {
this.rAF && window.cancelAnimationFrame(this.rAF)
return
}
this.draw()
this.rAF = window.requestAnimationFrame(this.loop)
}
draw() {
this.step()
this.ctx.globalCompositeOperation = 'destination-out'
this.ctx.fillStyle = 'hsla(0, 0%, 0%, 0.6)'
this.ctx.fillRect(0, 0, this.width, this.height)
this.ctx.globalCompositeOperation = 'lighter'
this.ctx.save()
this.ctx.translate(this.width / 2, this.height / 2)
this.ctx.rotate(this.tick / 300)
let i = this.parts.length
while (i--) {
this.parts[i].draw(this)
}
this.ctx.restore()
}
step() {
if (this.tick % 2 === 0 && this.parts.length < 200) {
this.parts.push(new Particle())
}
let i = this.parts.length
while (i--) {
this.parts[i].step(this)
}
this.tick += this.mouse.down ? 3 : 1
}
mousedown() {
this.mouse.down = 1
}
mouseup() {
this.mouse.down = 0
}
resize() {
this.tick = 0
this.width = window.innerWidth
this.height = window.innerHeight
this.canvas.width = this.width
this.canvas.height = this.height
this.mouse.down = 0
this.fl = 300
this.bounds = {
x: {min: -this.width / 2, max: this.width / 2},
y: {min: -this.height / 2, max: this.height / 2},
z: {min: -this.fl, max: 1000}
}
this.parts.length = 0
}
}

@ -1,16 +1,11 @@
export default {
path: 'directive',
meta: {title: '全局指令', icon: 'el-icon-s-grid'},
meta: {title: '全局指令', icon: 'el-icon-s-grid', alwaysShow: true},
children: [
{
path: 'dragDialog',
component: 'example/directive/dragDialog',
meta: {title: '可拖拽dialog'}
},
{
path: 'ripple',
component: 'example/directive/ripple',
meta: {title: '波纹'}
}
]
}

@ -1,21 +1,11 @@
export default {
path: 'global',
meta: {title: '全局方法', icon: 'el-icon-s-opportunity'},
meta: {title: '全局方法', icon: 'el-icon-s-opportunity', alwaysShow: true},
children: [
{
path: 'message',
component: 'example/globalMethod/message',
meta: {title: '消息提示'}
},
{
path: 'imageViewer',
component: 'example/globalMethod/imageViewer',
meta: {title: '图片预览'}
},
{
path: 'verify',
component: 'example/globalMethod/verify',
meta: {title: '拼图验证'}
}
]
}

@ -1,14 +0,0 @@
import {createMutations} from "@/store/util"
const state = {
//登录页背景动画
loginBackgroundAnimation: 'sparkRain',
}
const mutations = createMutations(state)
export default {
namespaced: true,
state,
mutations
}

@ -88,8 +88,7 @@ export default {
this.$refs.form.validate(valid => {
if (!valid) return
this.loading = true
this.$verify()
.then(() => this.$store.dispatch('user/login', this.form))
this.$store.dispatch('user/login', this.form)
.then(this.success)
.catch(() => this.loading = false)
})

@ -1,49 +0,0 @@
<script type="text/jsx">
const animations = [
{name: '无', value: ''},
{name: '烟花', value: 'firework'},
{name: '上帝之光', value: 'godrays'},
{name: '简单粒子', value: 'moveFollowMouse'},
{name: '球', value: 'particleBall'},
{name: '网络', value: 'particleNetwork'},
{name: '波浪', value: 'particleWave'},
{name: '雨', value: 'reflectRain'},
{name: '流星雨', value: 'sparkRain'},
{name: '阳光', value: 'sunlight'},
]
export default {
name: "SetAnimation",
functional: true,
props: {
value: String,
customClass: String
},
render(h, context) {
const {value, customClass} = context.props
return (
<el-dropdown class={customClass} size="medium" trigger="click" onCommand={context.listeners.select}>
<el-button type="text" title="选择背景动画">
<v-icon icon="svg-show"/>
</el-button>
<el-dropdown-menu slot="dropdown">
{
animations.map(animation => (
<el-dropdown-item
key={animation.value}
disabled={animation.value === value}
command={animation.value}
>
{animation.name}
</el-dropdown-item>
))
}
</el-dropdown-menu>
</el-dropdown>
)
}
}
</script>

@ -1,11 +1,8 @@
<template>
<div class="login-page">
<canvas id="login-background"/>
<div class="login-container" @click.stop>
<div class="title">
{{ title }}
<set-animation :value="loginBackgroundAnimation" custom-class="set-animation" @select="setAnimation"/>
</div>
<component :is="component"/>
@ -14,55 +11,25 @@
</template>
<script>
import {mapState} from 'vuex'
import {title} from '@/config'
import SetAnimation from "./SetAnimation"
import LoginForm from "./LoginForm"
import RegisterForm from "./RegisterForm"
import {isEmpty} from "@/util"
import {isMobile} from "@/util/browser"
export default {
name: 'login',
components: {LoginForm, RegisterForm, SetAnimation},
components: {LoginForm, RegisterForm},
data: () => ({title}),
computed: {
...mapState('app', ['loginBackgroundAnimation']),
component() {
const formType = this.$route.path.substring(1)
return `${[...formType].join('')}-form`
}
},
methods: {
clearAnimation() {
if (this.animationInstance) {
this.animationInstance.stop()
this.animationInstance = null
}
},
setAnimation(value) {
this.clearAnimation()
this.$store.commit('app/loginBackgroundAnimation', value)
if (isEmpty(value)) return
import(`@/plugin/canvasAnimation/${value}`)
.then(_ => this.animationInstance = new _.default(document.getElementById('login-background')))
}
},
mounted() {
this.animationInstance = null
//
!isMobile() && this.setAnimation(this.loginBackgroundAnimation)
},
beforeDestroy() {
this.clearAnimation()
this.$message.closeAll()
}
}

@ -5,25 +5,12 @@ $light_gray: #ffffff;
$dark_gray: #889aa4;
$cursor: #ffffff;
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
.login-container .el-input input {
color: $cursor;
}
}
.login-page {
display: flex;
flex-direction: column;
height: 100%;
overflow: auto;
canvas {
position: absolute;
height: 100%;
width: 100%;
z-index: -1;
background: #2d3a4b;
}
.login-container {
flex: 1;
@ -41,15 +28,6 @@ $cursor: #ffffff;
color: #eee;
font-weight: bold;
position: relative;
.set-animation {
color: #ffffff;
position: absolute;
top: 115px;
font-size: 18px;
right: 10px;
cursor: pointer;
}
}
.svg-container {

@ -1,57 +0,0 @@
<template>
<div>
<div class="tip-row">
波纹详情请见<a href="https://quasar.dev/vue-directives/material-ripple" target="_blank">quasar</a>
</div>
<div v-ripple.early class="ripple-example" :style="style">点我触发</div>
</div>
</template>
<script>
export default {
name: "ripplePage",
data() {
return {
index: 0,
colors: ['amber', 'orange', 'accent', 'lime', 'cyan', 'purple', 'brown', 'blue']
}
},
computed: {
style() {
return {'background-color': this.colors[this.index]}
}
},
mounted() {
this.timer = window.setInterval(() => {
this.index = (this.index + 1) % this.colors.length
this.color = this.colors[this.index]
}, 3000)
this.$once('hook:beforeDestroy', () => {
window.clearInterval(this.timer)
})
}
}
</script>
<style scoped>
.ripple-example {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
margin-top: 50px;
height: 150px;
width: 80%;
max-width: 500px;
border-radius: 3px;
cursor: pointer;
transition: background 1.5s;
color: #ffffff;
user-select: none;
}
</style>

@ -1,33 +0,0 @@
<template>
<div>
<div class="tip-row">
这是对<a href="https://github.com/sachinchoolur/lightgallery.js" target="_blank">lightgallary</a>的简单封装
</div>
<div>
<img v-for="(i,index) in images" :key="i" :src="i" @click="preview(index)">
</div>
</div>
</template>
<script>
export default {
name: "imageViewerPage",
data() {
return {
images: [
'https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=4018557288,1217151095&fm=26&gp=0.jpg',
'https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3252521864,872614242&fm=26&gp=0.jpg',
'https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2534506313,1688529724&fm=26&gp=0.jpg'
]
}
},
methods: {
preview(index) {
this.$image({index, urlList: this.images})
}
}
}
</script>

@ -1,25 +0,0 @@
<template>
<div>
<div class="tip-row">
拼图验证
<a href="https://github.com/Kevin-269581661/vue-puzzle-verification" target="_blank"></a>的基础上做了样式修改
</div>
<el-button type="primary" @click="onClick">触发验证</el-button>
</div>
</template>
<script>
import {elSuccess, elError} from "@/util/message"
export default {
name: "verifyPage",
methods: {
onClick() {
this.$verify()
.then(() => elSuccess('验证通过!'))
.catch(() => elError('验证失败!'))
}
}
}
</script>
Loading…
Cancel
Save