feat: svg学习笔记

This commit is contained in:
NICE CODE BY DEV 2022-08-15 18:07:53 +08:00
parent 82a59b47a6
commit 63e96b7854
23 changed files with 144 additions and 1358 deletions

View File

@ -1,898 +0,0 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 canvas
order: 6
---
## 动画案例
### 粒子背景
<code src="./demos/demo3/index.jsx" />
### 粒子图片
<code src="./demos/demo2/index.jsx" />
### 贪吃蛇
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
const canvasRef = useRef()
async function init() {
var snake = [41, 40], //snake队列表示蛇身初始节点存在但不显示
direction = 1, //1表示向右-1表示向左20表示向下-20表示向上
food = 43, //食物的位置
n, //与下次移动的位置有关
box = canvasRef.current && canvasRef.current.getContext('2d');
//从0到399表示box里[0~19]*[0~19]的所有节点每20px一个节点
function draw(seat, color) {
box.fillStyle = color;
box.fillRect(seat % 20 *20 + 1, ~~(seat / 20) * 20 + 1, 18, 18);
//用color填充一个矩形以前两个参数为xy坐标后两个参数为宽和高。
}
document.onkeydown = function(evt) { //当键盘上下左右键摁下的时候改变direction
direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;
console.log([-1, -20, 1, 20][(evt || event).keyCode - 37]);
};
function _move() {
snake.unshift(n = snake[0] + direction); //此时的n为下次蛇头出现的位置n进入队列
if(snake.indexOf(n, 1) > 0 || n < 0 || n > 399 || direction == 1 && n % 20 == 0 || direction == -1 && n % 20 == 19) {
//if语句判断贪吃蛇是否撞到自己或者墙壁碰到时返回结束程序
return alert("GAME OVER!");
}
draw(n, "lime"); //画出蛇头下次出现的位置
if(n == food) { //如果吃到食物时,产生一个蛇身以外的随机的点,不会去掉蛇尾
while (snake.indexOf(food = ~~(Math.random() * 400)) > 0);
draw(food, "yellow");
} else { //没有吃到食物时正常移动,蛇尾出队列
draw(snake.pop(),"black");
}
setTimeout(() => _move(), 150); //每隔0.15秒执行函数一次,可以调节蛇的速度
}
box && await _move()
}
return (
<div style={{ background: '#000' }}>
<button type="button" onClick={() => init()} >开始</button>
<canvas ref={canvasRef} width="400" height="400" />
</div>
)
}
```
### 液体海报
<code src="./demos/demo1/index.jsx" />
### 大转盘doing
```jsx
import React, { useRef, useEffect } from 'react';
class Global {
constructor () {};
/**
* 判断是否为 PC 端,若是则返回 true否则返回 flase
*/
IsPC() {
let userAgentInfo = navigator.userAgent,
flag = true,
Agents = ["Android", "iPhone","SymbianOS", "Windows Phone","iPad", "iPod"];
for (let v = 0; v < Agents.length; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
};
/**
* 缓动函数,由快到慢
* @param {Num} t 当前时间
* @param {Num} b 初始值
* @param {Num} c 变化值
* @param {Num} d 持续时间
*/
easeOut(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t + b;
return -c / 2 * ((--t) * (t - 2) - 1) + b;
};
windowToCanvas(canvas, e) {
let bbox = canvas.getBoundingClientRect(),
x = this.IsPC() ? e.clientX || event.clientX : e.changedTouches[0].clientX,
y = this.IsPC() ? e.clientY || event.clientY : e.changedTouches[0].clientY;
return {
x: x - bbox.left,
y: y - bbox.top
}
};
/**
* 绘制自动换行的文本
* @param {Obj} context
* @param {Str} t 文本内容
* @param {Num} x 坐标
* @param {Num} y 坐标
* @param {Num} w 文本限制宽度
* @param {Num} lineHeight 行高
*/
drawText(context, t, x, y, w, lineHeight = 20){
let chr = t.split(''),
temp = '',
row = [];
for (let a = 0; a < chr.length; a++){
if ( context.measureText(temp).width < w ) {
;
}
else{
row.push(temp);
temp = '';
}
temp += chr[a];
};
row.push(temp);
for(let b = 0; b < row.length; b++){
context.fillText(row[b], x, y + (b + 1) * lineHeight);
};
};
/**
* 定义圆角矩形的方法
* @param {Obj} context
* @param {Num} cornerX
* @param {Num} cornerY
* @param {Num} width
* @param {Num} height
* @param {Num} cornerRadius
*/
roundedRect(context, cornerX, cornerY, width, height, cornerRadius) {
if (width > 0) context.moveTo(cornerX + cornerRadius, cornerY);
else context.moveTo(cornerX - cornerRadius, cornerY);
context.arcTo(cornerX + width, cornerY,
cornerX + width, cornerY + height,
cornerRadius);
context.arcTo(cornerX + width, cornerY + height,
cornerX, cornerY + height,
cornerRadius);
context.arcTo(cornerX, cornerY + height,
cornerX, cornerY,
cornerRadius);
if (width > 0) {
context.arcTo(cornerX, cornerY,
cornerX + cornerRadius, cornerY,
cornerRadius);
}
else {
context.arcTo(cornerX, cornerY,
cornerX - cornerRadius, cornerY,
cornerRadius);
}
}
}
class RouletteWheel extends Global{
constructor(params) {
super()
this.width = params.width
this.height = params.height
this.centerX = params.centerX
this.centerY = params.centerY
this.outsideRadius = params.outsideRadius
this.evenColor = params.evenColor
this.oddColor = params.oddColor
this.loseColor = params.odd
this.textColor = params.textColor
this.awards = params.awards || []
this.startRadian = params.startRadian || 0
this.duration = params.duration || 4000
this.velocity = params.velocity || 10
// 回调函数
this.finish = params.finish
}
initCanvas() {
let canvas = this.canvas
canvas.width = this.width;
canvas.height = this.height;
let ctx = canvas.getContext('2d')
for (let i = 0; i < this.awards.length; i++) {
// const award = awards[i]
let _startR = this.startRadian + this.awardRadian * i
let _endR = _startR + this.awardRadian
if (i % 2 === 0) ctx.fillStyle = "#FF6766"
else ctx.fillStyle = "#FD5757"
ctx.beginPath(); //开始绘制路径
ctx.moveTo(250, 250); //将当前位置移动到新的目标点
ctx.arc(250, 250, this.radius, _startR, _endR);
ctx.closePath(); //绘制路径
ctx.fill();
}
ctx.beginPath(); //开始绘制路径
ctx.moveTo(250, 250); //将当前位置移动到新的目标点
ctx.arc(250, 250, 250, Math.PI / 2, Math.PI);
ctx.closePath(); //绘制路径
ctx.fillStyle = "#ccc"; //填充背景颜色
ctx.fill();
ctx.beginPath(); //开始绘制路径
ctx.moveTo(250, 250); //将当前位置移动到新的目标点
ctx.arc(250, 250, 250, Math.PI, Math.PI * 1.5);
ctx.closePath(); //绘制路径
ctx.fillStyle = "#ddd"; //填充背景颜色
ctx.fill();
ctx.beginPath(); //开始绘制路径
ctx.moveTo(250, 250); //将当前位置移动到新的目标点
ctx.arc(250, 250, 250, Math.PI * 1.5, Math.PI * 2);
ctx.closePath(); //绘制路径
ctx.fillStyle = "#aaa"; //填充背景颜色
ctx.fill();
}
}
export default () => {
const canvasRef = useRef()
useEffect(() => {
let rw = new RouletteWheel({
canvas: canvasRef.current,
width: '500',
height: '500',
awards: [ // 转盘内的奖品个数以及内容
'大保健', '话费10元', '话费20元', '话费30元', '保时捷911', '周大福土豪金项链',
// 'iphone 20', '火星7日游'
]
})
}, [])
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
)
}
```
### 火焰
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
const canvasRef = useRef()
function init() {
var c = canvasRef.current,
$ = c.getContext('2d'),
w = c.width = window.innerWidth / 2,
h = c.height = window.innerHeight / 2,
particles = []
/**
* 随机获取颜色
*
* @returns rgb(x,x,x)
*/
function randomColor() {
var r = 100 + Math.floor(Math.random() * 255),
g = Math.floor(Math.random() * 150),
b = Math.floor(Math.random() * 15)
return 'rgb(' + r + ',' + g + ',' + b + ')'
}
function particle() {
this.location = {
x: w / 2,
y: h / 2
}
this.speed = {
x: -1.5 + Math.random() * 3,
y: 1 + Math.random() * 5.5
}
this.life = 50
this.radius = 1 + Math.floor(Math.random() * 25)
this.color = randomColor()
this.opacity = 1
this.dead = false
this.draw = function () {
$.globalCompositeOperation = 'lighter'
$.fillStyle = this.color
$.beginPath()
$.arc(this.location.x, this.location.y, this.radius, 0, Math.PI * 2)
$.globalAlpha = this.opacity
$.fill()
$.closePath()
}
this.update = function () {
if (this.location.x < 0 || this.life == 0 || this.opacity === 0 || this.radius < 1) {
this.dead = true
}
if (!this.dead) {
this.location.x += this.speed.x
this.location.y -= this.speed.y
this.life--
this.opacity -= 0.05
this.radius--
}
}
}
// 将火焰置于背景之后
function stage() {
$.globalCompositeOperation = 'source-over'
$.fillStyle = 'rgba(0, 0, 0, 1)'
$.fillRect(0, 0, w, h)
}
// 重置画布大小
function reset() {
w = c.width = window.innerWidth / 2
h = c.height = window.innerHeight / 2
}
function loop() {
stage()
var L = particles.length
if (L < 100) {
particles.push(new particle())
}
for (var i = 0; i < L; i++) {
var p = particles[i]
p.draw()
p.update()
if (p.dead) {
particles[i] = new particle()
}
}
requestAnimationFrame(loop)
}
function _init() {
reset()
loop()
}
window.addEventListener('resize', reset)
_init()
}
useEffect(() => {
init()
}, [])
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
)
}
```
### 星空
```jsx
import React, { useRef, useEffect } from 'react';
/**
* 星空初始化
*/
class NightSky {
constructor(opt) {
this.opt = {
width: 500,
height: 500,
num: 120,
canvas: null,
...opt
}
this.opt.canvas.width = this.opt.width
this.opt.canvas.height = this.opt.height
this.ctx = this.opt.canvas && this.opt.canvas.getContext('2d')
this.opt.canvas.style.backgroundColor = '#000'
this.starList = []
this.draw = this.draw
this.init()
}
init() {
this.drawStar()
this.animate()
}
drawStar() {
let { width, height, num } = this.opt
for (let i = 0; i < num; i++) {
this.starList[i] = new Star({
maxRadius: 3,
ctx: this.ctx,
width,
height
})
this.starList[i].draw()
}
}
animate() {
let ctx = this.ctx
let starList = this.starList
let { width, height } = this.opt
function _move() {
ctx.clearRect(0, 0, width, height)
for (const i in starList) {
starList[i].move()
}
window.requestAnimationFrame(_move)
}
window.requestAnimationFrame(_move)
}
draw(val) {
return val
}
}
class Star {
constructor(opt) {
let { width, height, maxRadius = 2, ctx, speed = 0.5 } = opt
this.x = Math.random() * width
this.y = Math.random() * height
this.height = height
this.width = width
this.speed = speed
this.maxRadius = maxRadius
this.ctx = ctx
this.r = Math.random() * maxRadius
var alpha = (Math.floor(Math.random() * 10) + 1) / 10
this.color = `rgba(255, 255, 255, ${alpha})`
}
draw() {
this.ctx.fillStyle = this.color
this.ctx.shadowBlur = this.r * 2
this.ctx.beginPath()
this.ctx.arc(this.x, this.y, this.r * Math.random(), 0, 2 * Math.PI, false)
this.ctx.closePath()
this.ctx.fill()
}
move() {
this.y -= this.speed
if (this.y <= -10) {
this.y = this.height + 10
}
this.draw()
}
}
export default () => {
const canvasRef = useRef()
useEffect(() => {
let nightSky = new NightSky({
canvas: canvasRef.current,
width: 500,
height: 300
})
}, [])
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
)
}
```
### 移动doing
```jsx
import React, { useRef, useEffect } from 'react';
class Move {
constructor(opt) {
const option = {
canvas: null,
width: document.documentElement.clientWidth, // 宽度
height: document.documentElement.clientHeight, // 高度
bgColor: '#000',
para: {
num: 100,
color: false, // 颜色 如果是false 则是随机渐变颜色
r: 0.9, // 圆每次增加的半径
o: 0.09, // 判断圆消失的条件,数值越大,消失的越快
},
...opt
}
const { canvas, width, height, bgColor } = option
this.option = option
this.round_arr = []
this.ctx = canvas.getContext('2d')
canvas.width = width
canvas.height = height
canvas.style.backgroundColor = bgColor
this.init(this)
}
init(opt) {
let tempSum = 0
window.onmousemove = function (event) {
let mouseX = event.clientX;
let mouseY = event.clientY;
if (tempSum < 5) {
tempSum++
} else {
opt.round_arr.push({
mouseX,
mouseY,
r: opt.option.para.r, // 设置半径每次增大的数值
o: 1, // 判断圆消失的条件,数值越大,消失得越快
})
tempSum = 0
opt.animate()
}
};
}
animate() {
let { para, width, height } = this.option
let color = 0, color2
let ctx = this.ctx
let round_arr = this.round_arr
if (!para.color) {
color += Math.random();
color2 = 'hsl(' + color + ',100%,80%)';
}
function _move() {
ctx.clearRect(0, 0, width, height);
for (var i = 0; i < round_arr.length; i++) {
ctx.fillStyle = color2;
ctx.beginPath();
ctx.arc( round_arr[i].mouseX ,round_arr[i].mouseY, round_arr[i].r, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
round_arr[i].r += para.r;
round_arr[i].o -= para.o;
if( round_arr[i].o <= 0){
round_arr.splice(i,1);
i--;
}
}
}
window.requestAnimationFrame(_move);
}
}
export default () => {
const canvasRef = useRef()
useEffect(() => {
let rw = new Move({
canvas: canvasRef.current,
width: 500,
height: 300
})
}, [])
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
)
}
```
### 棒棒糖
```jsx
import React, { useRef, useEffect } from 'react';
class Lollipop {
constructor(opt) {
this.opt = {
canvas: null, // 画布
width: document.documentElement.clientWidth, // 宽度
height: document.documentElement.clientHeight, // 高度
bgColor: '#000',
...opt
}
this.ctx = this.opt.canvas.getContext('2d')
// 初始化画布
this.opt.canvas.width = this.opt.width
this.opt.canvas.height = this.opt.height
this.opt.canvas.style.backgroundColor = this.opt.bgColor
this.render()
}
render() {
this._drawCircle(this.ctx)
this._drawStick(this.ctx)
this._drawHalfCircle(this.ctx)
}
/**
* 画圆
* @param {*} ctx
*/
_drawCircle(ctx) {
ctx.beginPath()
ctx.arc(300, 300, 50, 0, Math.PI * 2, true)
ctx.closePath()
ctx.fillStyle = '#fff'
ctx.shadowBlur = 15
ctx.shadowColor = '#fff'
ctx.fill()
}
/**
* 棍子
* @param {*} ctx
*/
_drawStick(ctx) {
ctx.beginPath()
ctx.moveTo(340, 340)
ctx.lineTo(450, 450)
ctx.lineWidth = 8
ctx.lineCap = 'round'
ctx.strokeStyle = '#fff'
ctx.stroke()
ctx.closePath()
}
_drawHalfCircle(ctx) {
ctx.beginPath()
ctx.arc(300, 300, 30, 0, Math.PI * 0.6, false)
ctx.shadowBlur = 5
ctx.lineWidth = 5
ctx.lineCap = 'round'
ctx.strokeStyle = '#ccc'
ctx.stroke()
}
}
export default () => {
const canvasRef = useRef()
useEffect(() => {
new Lollipop({
canvas: canvasRef.current,
width: 500,
height: 800
})
}, [])
return (
<div>
<canvas ref={canvasRef} width="200" height="200" />
</div>
)
}
```
### 行星动画
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
const solarRef = useRef()
function init() {
let sun = new Image()
let moon = new Image()
let earth = new Image()
sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png'
moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png'
earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png'
window.requestAnimationFrame(solar)
// 太阳系动画
function solar() {
let canvas = solarRef && solarRef.current
let ctx = canvas.getContext('2d')
// 将目标图形置于上层
ctx.globalCompositeOperation = 'destination-over'
ctx.clearRect(0, 0, 300, 300)
ctx.fillStyle = 'rgba(0, 0, 0, 0.4)'
ctx.strokeStyle = 'rgba(0, 153, 255, 0.4)'
// 保存当前状态
ctx.save()
// 将当前画笔移到圆心处
ctx.translate(150, 150)
let time = new Date()
// 地球顺时针旋转的角度
ctx.rotate(((2 * Math.PI) / 60) * time.getSeconds() + ((2 * Math.PI) / 60000) * time.getMilliseconds())
// 从当前圆心处 x 偏移量
ctx.translate(105, 0)
// 绘制当图片加载不出来 默认图
// ctx.fillRect(0, -12, 25, 25)
// 绘制地球的图片
ctx.drawImage(earth, -12, -12)
// 保存当前位置
// 基于地球为圆心的旋转角度
ctx.rotate(((2 * Math.PI) / 60) * time.getSeconds() + ((2 * Math.PI) / 60000) * time.getMilliseconds())
// 以地球为圆心的偏移
ctx.translate(0, 28.5)
// 绘制月亮
ctx.drawImage(moon, -3.5, -3.5)
// 加载上一次的 save
ctx.restore()
ctx.beginPath()
ctx.arc(150, 150, 105, 0, Math.PI * 2, false)
ctx.stroke()
ctx.drawImage(sun, 0, 0, 300, 300)
window.requestAnimationFrame(solar)
}
}
useEffect(() => {
setTimeout(() => init(), 1000)
}, [solarRef])
return (
<div>
<canvas ref={solarRef} width="300" height="300" />
</div>
)
}
```
### 时钟
```jsx
import React, { useRef, useEffect } from 'react';
export default () => {
const clockRef = useRef()
function clock() {
var theCanv = clockRef.current;
var theCanvObject = theCanv.getContext('2d');
var x = 200;
var y = 200;
startTime();
function startTime() {
//分秒刻度和表盘
theCanvObject.lineWidth = 1;
for (var i = 0; i < 60; i++) {
drawArc(150, i*6, (i+1)*6);
}
drawArc(145, 0, 360, true);
//时刻度
theCanvObject.lineWidth = 2;
for (var i = 0; i < 12; i++) {
drawArc(150, i*30, (i+1)*30);
}
drawArc(140, 0, 360, true);
//针
drawHand(getTime().hour,5,60,'#ECFC00');
drawHand(getTime().min,4,100,'#00BB3F');
drawHand(getTime().sec,3,130,'#D60062');
setInterval(function () {
drawArc(135,0,360,true);
drawHand(getTime().hour,5,60,'#ECFC00');
drawHand(getTime().min,4,100,'#00BB3F');
drawHand(getTime().sec,3,130,'#D60062');
},1000);
}
function drawArc(iRadius, iBeginAngle, iEndAngle, ifClear) {
var beginRadian = iBeginAngle*Math.PI/180;
var endRadian = iEndAngle*Math.PI/180;
theCanvObject.beginPath(); //创建一个路径
theCanvObject.moveTo(x, y); //将路径移到xy
theCanvObject.arc(x, y, iRadius, beginRadian, endRadian, false);
//画弧
!ifClear && theCanvObject.stroke();
if (ifClear) {
theCanvObject.fillStyle = 'white';
theCanvObject.fill();
}
}
function drawHand(iAngle, iWidth, iLength, iColor) {
theCanvObject.save(); //保存的是canvas的属性不是截图
theCanvObject.lineWidth = iWidth;
theCanvObject.strokeStyle = iColor;
drawArc(iLength, iAngle, iAngle);
theCanvObject.restore(); //弹出栈中的状态
}
//根据当前时间返回各个针要指的度数
function getTime() {
var jTime = {};
var iNow = new Date();
jTime.sec = -90 + iNow.getSeconds()*6;
jTime.min = -90 + iNow.getMinutes()*6 + iNow.getSeconds()/20;
jTime.hour = -90 + iNow.getHours()*30 + iNow.getMinutes()/2;
return jTime;
}
}
useEffect(() => {
clock()
}, [])
return (
<div>
<canvas ref={clockRef} width="600" height="600" />
</div>
)
}
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

View File

@ -1,118 +0,0 @@
import React, { useRef, useEffect, FC } from 'react';
import './index.less'
const img1 = require('./img/clipImg1.jpg')
const img2 = require('./img/clipImg2.jpg')
export default () => {
const canvasRef = useRef()
const clipImgs1Ref = useRef()
const clipImgs2Ref = useRef()
function clipPathMaskRender() {
const NUM_CIRCLES = 60
const MIN_SIZE = 50
const MAX_SIZE = 100
function getRndInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
var c = canvasRef && canvasRef.current
var ctx = c.getContext('2d')
var clipImg1 = clipImgs1Ref && clipImgs1Ref.current
var clipImg2 = clipImgs2Ref && clipImgs2Ref.current
var t
class Circle {
constructor() {
this.x = 0
this.y = 0
this.size = 0
this._needsRandomized = false
}
randomize () {
this.x = getRndInt(50, c.width - 50)
this.y = getRndInt(50, c.height - 50)
this.maxSize = getRndInt(MIN_SIZE, MAX_SIZE)
}
//
update (t, ofs) {
// abs
this.size = Math.abs(Math.round(Math.sin(t + ofs) * this.maxSize))
if (this.size < 2) {
if (this._needsRandomized) {
this.randomize()
this._needsRandomized = false
}
}
}
//
draw () {
ctx.moveTo(this.x, this.y)
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI)
}
}
var circles =[]
for (let i = 0; i < NUM_CIRCLES; i++) {
var circle = new Circle()
circle.randomize()
circles.push(circle)
}
function update() {
t = 0.001 * Date.now()
circles.forEach((circle, idx) => {
circle.update(t, idx)
})
}
async function render() {
await ctx.drawImage(clipImg1, 0, 0)
await ctx.save()
await ctx.beginPath()
circles.forEach(function(circle) {
circle.draw()
})
ctx.closePath()
ctx.clip()
ctx.drawImage(clipImg2, 0, 0)
ctx.restore()
}
function loop() {
requestAnimationFrame(loop)
update()
render()
}
loop()
}
useEffect(() => {
clipPathMaskRender()
}, [])
return (
<div className="clipPathMask demo">
<div className="wrap">
<h1 className="wrap_tit">Nice Note</h1>
<canvas ref={canvasRef} className="wrap_canvas" width="500px" height="500px"></canvas>
</div>
<div id="clipImgs">
<img ref={clipImgs1Ref} id="clipImgs_1" crossOrigin="anonymous" src={img1} />
<img ref={clipImgs2Ref} id="clipImgs_2" crossOrigin="anonymous" src={img2} />
</div>
</div>
)
}

View File

@ -1,18 +0,0 @@
.clipPathMask {
text-align: center;
height: 80vh;
color: #fff;
background: teal linear-gradient(transparent, #ff0099);
.wrap {
&_tit {
padding: 10px 0;
font-size: 30px;
}
&_canvas {
border: 1px solid yellow;
}
}
#clipImgs {
display: none;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,217 +0,0 @@
import React, { useRef, useEffect } from 'react';
import img1 from './imgs/1.jpg'
import img2 from './imgs/2.jpg'
import img3 from './imgs/3.jpg'
const imgs = [img1, img2, img3]
class Particale {
constructor(opt) {
this.warp = opt.warp; //
this.ctx = opt.warp && opt.warp.getContext('2d');
this.imgsUrl = opt.imgsUrl; //
this.imgsObj = []; //
this.radius = opt.radius || 10; //
this.index = 0; //
this.initz = 300;
this.dots = [];
this.init();
}
init() {
//
this.warp.style.marginLeft = "calc(50vw - 500px)";
this.warp.style.marginTop = "calc(50vh - 300px)";
//
if (this.warp.width > 500 || this.warp.height > 300)
this.radius >= 4 ? this.radius = this.radius : this.radius = 4;
else
this.radius >= 2 ? this.radius = this.radius : this.radius = 2;
let promiseArr = this.imgsUrl.map(imgUrl => {
return new Promise((resolve, reject) => {
var imgObj = new Image();
imgObj.onload = () => {
this.imgsObj.push(imgObj);
resolve();
};
imgObj.src = imgUrl;
});
});
//
Promise.all(promiseArr).then(() => {
this.picLoop();
});
}
picLoop() {
this.dots = [];
this.drawPic(); //
this.toParticle(); //
this.combineAnimate(); //
this.index === this.imgsUrl.length-1 ? this.index = 0 : this.index++; //
}
drawPic() {
//
this.ctx.clearRect(0, 0, this.warp.width, this.warp.height);
let imgObj = this.imgsObj[this.index];
//
if(imgObj.width > imgObj.height) {
let ImgScale = imgObj.height / imgObj.width;
imgObj.width = this.warp.width * .5;
imgObj.height = imgObj.width * ImgScale;
} else {
let ImgScale = imgObj.width / imgObj.height;
imgObj.height = this.warp.height * .7;
imgObj.width = imgObj.height * ImgScale;
}
//canvas
this.ctx.drawImage(imgObj, this.warp.width / 2 - imgObj.width / 2, this.warp.height / 2 - imgObj.height / 2, imgObj.width, imgObj.height);
}
toParticle() {
//
let imageData = this.ctx.getImageData(0, 0, this.warp.width, this.warp.height);
let data = imageData.data;
for(let x = 0; x < imageData.width; x += this.radius * 2) {
for(let y = 0; y < imageData.height; y += this.radius * 2) {
let i = (x + y * this.warp.width) * 4;
if(data[i+3] !== 0 && data[i] !== 255 && data[i+1] !== 255 && data[i+2] !== 255) {
let dot = {
x: x, //x
y: y, // y
z: 0, // z
r: data[i], // rgba
g: data[i+1], // rgba
b: data[i+2], // rgba
a: 1, // rgba
ix: Math.random() * this.warp.width, //x
iy: Math.random() * this.warp.height, // y
iz: Math.random() * this.initz * 2 - this.initz, // z
ir: 255, // rgba
ig: 255, // rgba
ib: 255, // rgba
ia: 0, // rgba
tx: Math.random() * this.warp.width, //x
ty: Math.random() * this.warp.height, // y
tz: Math.random() * this.initz * 2 - this.initz, // z
tr: 255, // rgba
tg: 255, // rgba
tb: 255, // rgba
ta: 0, // rgba
};
this.dots.push(dot);
}
}
}
}
combineAnimate() {
let combined = false;
this.ctx.clearRect(0, 0, this.warp.width, this.warp.height);
this.dots.map(dot => {
if (Math.abs(dot.ix - dot.x) < 0.1 && Math.abs(dot.iy - dot.y) < 0.1 && Math.abs(dot.iz - dot.z) < 0.1) {
dot.ix = dot.x;
dot.iy = dot.y;
dot.iz = dot.z;
dot.ir = dot.r;
dot.ig = dot.g;
dot.ib = dot.b;
dot.ia = dot.a;
combined = true;
} else {
dot.ix += (dot.x - dot.ix) * 0.07;
dot.iy += (dot.y - dot.iy) * 0.07;
dot.iz += (dot.z - dot.iz) * 0.07;
dot.ir += (dot.r - dot.ir) * 0.3;
dot.ig += (dot.g - dot.ig) * 0.3;
dot.ib += (dot.b - dot.ib) * 0.3;
dot.ia += (dot.a - dot.ia) * 0.1;
combined = false;
}
return this.drowDot(dot);
});
if(!combined) {
requestAnimationFrame(() => {
return this.combineAnimate();
});
} else {
setTimeout(() => {
return this.separateAnimate();
}, 1500);
}
}
separateAnimate() {
let separated = false;
this.ctx.clearRect(0, 0, this.warp.width, this.warp.height);
this.dots.map(dot => {
if (Math.abs(dot.ix - dot.tx) < 0.1 && Math.abs(dot.iy - dot.ty) < 0.1 && Math.abs(dot.iz - dot.tz) < 0.1) {
dot.ix = dot.tx;
dot.iy = dot.ty;
dot.iz = dot.tz;
dot.ir = dot.tr;
dot.ig = dot.tg;
dot.ib = dot.tb;
dot.ia = dot.ta;
separated = true;
} else {
dot.ix += (dot.tx - dot.ix) * 0.07;
dot.iy += (dot.ty - dot.iy) * 0.07;
dot.iz += (dot.tz - dot.iz) * 0.07;
dot.ir += (dot.tr - dot.ir) * 0.02;
dot.ig += (dot.tg - dot.ig) * 0.02;
dot.ib += (dot.tb - dot.ib) * 0.02;
dot.ia += (dot.ta - dot.ia) * 0.03;
separated = false;
}
return this.drowDot(dot);
});
if(!separated) {
requestAnimationFrame(() => {
return this.separateAnimate();
});
} else {
setTimeout(() => {
return this.picLoop(); //使
}, 100);
}
}
drowDot(dot) {
let scale = this.initz / (this.initz + dot.iz);
this.ctx.save();
this.ctx.beginPath();
this.ctx.fillStyle = `rgba(${Math.floor(dot.ir)}, ${Math.floor(dot.ig)}, ${Math.floor(dot.ib)}, ${dot.ia})`;
this.ctx.arc(this.warp.width / 2 + (dot.ix - this.warp.width / 2) * scale, this.warp.height / 2 + (dot.iy - this.warp.height / 2) * scale, this.radius * scale, 0, Math.PI * 2);
this.ctx.fill();
this.ctx.closePath();
this.ctx.restore();
}
}
export default () => {
const canvasRef = useRef()
useEffect(() => {
if (canvasRef && canvasRef.current) {
new Particale({
warp: canvasRef && canvasRef.current,
imgsUrl: imgs,
radius: 1,
});
}
}, [])
return (
<canvas ref={canvasRef} width="500px" height="500px"></canvas>
)
}

View File

@ -1,98 +0,0 @@
import React, { useRef, useEffect } from 'react';
export default () => {
const canvasRef = useRef()
function init() {
const theCanvas = canvasRef.current,
ctx = theCanvas.getContext('2d'),
current_point = {
x: null, //x
y: null, //y
max: 20000,
};
let canvas_width = theCanvas.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
canvas_height = theCanvas.height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
random_points = [],
all_points = [];
theCanvas.style = "position: absolute; top: 0px; left: 0px;";
function draw() {
//
ctx.clearRect(0, 0, canvas_width, canvas_height);
let i,pi,x_dist,y_dist,dist,w;
//线线
random_points.forEach((p, index) => {
p.x += p.xa, //
p.y += p.ya,
//
p.xa *= p.x > canvas_width || p.x < 0 ? -1 : 1,
p.ya *= p.y > canvas_height || p.y < 0 ? -1 : 1,
ctx.fillRect(p.x - 0.5, p.y - 0.5, 1, 1); //
for(i = index + 1; i < all_points.length; i++ ) {
pi = all_points[i];
if(pi.x !== null && pi.y !== null) {
x_dist = p.x - pi.x;
y_dist = p.y - pi.y;
dist = x_dist * x_dist + y_dist * y_dist;
//线
dist < pi.max && (pi === current_point && dist >= pi.max / 2 && (p.x -= 0.03 * x_dist, p.y -= 0.03 * y_dist));
//线使
w = (pi.max - dist) / pi.max;
ctx.beginPath();
ctx.lineWidth = w / 2;
ctx.strokeStyle = `rgba(110,110,110,${w + 0.2})`;
ctx.moveTo(p.x, p.y);
ctx.lineTo(pi.x, pi.y);
ctx.stroke();
}
}
}),requestAnimationFrame(draw);
}
//
window.onmousemove = e => {
e = e || window.event;
current_point.x = e.clientX;
current_point.y = e.clientY;
};
window.onmouseout = () => {
current_point.x = null;
current_point.y = null;
};
//100
for(let i = 0; i < 100; i++ ) {
let x = Math.random() * canvas_width, //
y = Math.random() * canvas_height,
xa = 2 * Math.random() - 1, //x
ya = 2 * Math.random() - 1, //y
max = 6000; //线
random_points[i] = {x, y, xa, ya, max};
}
//
all_points = [...random_points,current_point];
//-
setTimeout(draw, 100);
}
useEffect(() => {
init()
}, [])
return (
<div style={{ position: 'relative' }}>
<canvas ref={canvasRef} width="500px" height="500px"></canvas>
</div>
)
}

View File

@ -0,0 +1,52 @@
.ok {
&_line {
stroke-dasharray:300,300;
stroke-dashoffset:300;
transition: all 2s linear;
}
&_checkmark {
width: 56px;
height: 56px;
border-radius: 50%;
display: block;
stroke-width: 2;
stroke: #fff;
margin: 10% auto;
box-shadow: inset 0px 0px 0px #7ac142;
animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both;
&__circle {
stroke-dasharray: 166;
stroke-dashoffset: 166;
stroke-width: 2;
stroke: #7ac142;
fill: none;
animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
}
&__check {
transform-origin: 50% 50%;
stroke-dasharray: 48;
stroke-dashoffset: 0;
animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
}
:global {
@keyframes stroke {
100% {
stroke-dashoffset: 0;
}
}
@keyframes fill {
100% {
box-shadow: inset 0px 0px 0px 30px #7ac142;
}
}
@keyframes scale {
0,100% {
transform: none;
}
50% {
transform: scale3d(1.1, 1.1, 1);
}
}
}
}
}

16
docs/fea/svg/demos/ok.tsx Normal file
View File

@ -0,0 +1,16 @@
import React from 'react';
import styles from './index.less'
export default () => {
return (
<div className={styles.ok}>
<svg x="0px" y="0px" width="300px" height="100px" viewBox="0 0 300 100" class="svg1">
<line className={styles.ok_line} x1="20" y1="50" x2="200" y2="50" stroke="#000" stroke-width="1" ></line>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
<circle class={styles.ok_checkmark__circle} cx="26" cy="26" r="25" fill="none"/>
<path className={styles.ok_checkmark__check} fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
</svg>
</div>
)
}

36
docs/fea/svg/index.md Normal file
View File

@ -0,0 +1,36 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 SVG
order: 3
path: /svg
---
## 基本介绍
```jsx
import React from 'react';
export default () => {
return (
<div>
<svg x="0px" y="0px" width="500px" height="360px" viewBox="0 0 500 100">
<rect x="10" y="5" fill="white" stroke="green" width="90" height="90"></rect>
<circle cx="170" cy="50" r="45" fill="black" stroke="red" ></circle>
<line fill="none" stroke="black" x1="230" y1="6" x2="260" y2="95"></line>
<ellipse cx="400" cy="50" rx="60" ry="30" />
<g>
<path id="svg_5" d="m148.60774,139.10039c28.24222,-69.1061 138.89615,0 0,88.8507c-138.89615,-88.8507 -28.24222,-157.9568 0,-88.8507z" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="red"/>
<path id="svg_6" d="m265.00089,146.09396l19.88082,-21.09307l21.11909,22.40665l21.11909,-22.40665l19.88101,21.09307l-21.11909,22.40684l21.11909,22.40684l-19.88101,21.09326l-21.11909,-22.40684l-21.11909,22.40684l-19.88082,-21.09326l21.11891,-22.40684l-21.11891,-22.40684z" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="#ccc"/>
</g>
</svg>
</div>
)
}
```
## 基础动画案例
<code src="./demos/ok.tsx" />

View File

@ -1,7 +1,7 @@
---
hero:
title: Nice Note
desc: 💊 Dev's 学习笔记
desc: 💊 Dev's 笔记
actions:
- text: 开始学习
link: /fea/website

View File

@ -1,7 +1,4 @@
---
# nav:
# title: 其它
# path: /other
group:
title: 💊 买房攻略 2017
order: 3

View File

@ -1,7 +1,4 @@
---
# nav:
# title: 其它
# path: /other
group:
title: 💊 买房攻略 2020
order: 1

View File

@ -32,3 +32,15 @@ Linux卸载脚本
```
rm HomebrewUninstall.sh ; wget https://gitee.com/cunkai/HomebrewCN/raw/master/HomebrewUninstall.sh ; bash HomebrewUninstall.sh
```
## 常用命令
* 搜索包brew search [package-name]
* 查看包的信息brew info [package-name]
* 安装终端应用\环境brew install [package-name]
* 安装GUI应用 brew install --cask [AppName]
* 更新所有包brew update
* 更新某个包brew upgrade [package-name]
* 卸载某个包brew uninstall [package-name]
* 列出所有安装的包brew list
* 清理所有过时软件brew cleanup

View File

@ -3,9 +3,34 @@ nav:
title: FC
path: /funny
group:
title: 💊
title: 说明
order: 3
path: /code
---
就是一些有意思的 code
# 说明
就是一些有意思的酷炫 code
## 迪士尼动画12原则
1. 挤压与拉伸
2. 预备动作
3. 表情与呈像方式
4. 逐帧画法与关键帧画法
5. 动作的惯性跟随和重叠
6. 慢入与慢出
7. 弧形运动轨迹
8. 次要动作
9. 节奏
10. 夸张
11. 熟练的手绘技法
12. 吸引力
## 提升页面性能技巧
页面渲染顺序js - css - layout重排 - paint重绘 - composite
* translate 替换为 left\top\right\bottom
* scale 替换为 width\height
* opacity 替换为 display\visibility