favicon摸魚工具開發0.2版本

蝸牛老溼發表於2020-04-07

前幾天沒啥事,寫了一個favicon看視訊的工具,挺多小老弟喜歡, 我就又更新了幾個功能,寫個完整版教程把,程式碼放github了 github.com/shengxinjin…

新增功能

  1. 視訊進度條,上下左右控制視訊音量和前進後退
  2. 攝像頭直播,新增了簡單的懷舊濾鏡?
  3. 貪食蛇小遊戲
    先看效果把
    favicon摸魚工具開發0.2版本
    favicon摸魚工具開發0.2版本
    favicon摸魚工具開發0.2版本

原理很簡單,favicon上可放一個32*32的圖片,我們動態修改這個圖,就可以實現放視訊,看攝像頭和小遊戲的功能,直接看下完整版程式碼把

放視訊

視訊功能見上個文章 juejin.im/post/5e8605…

音量和視訊控制

document監聽鍵盤事件,針對上下左右做不同的處理

const DIRECTION = {
    37:'left',
    // 上
    38:'up',
    // 右
    39:'right',
    // 下
    40:'down',
}
複製程式碼
const directions = {
  left: ()=> this.video.currentTime-=5,
  right: ()=> this.video.currentTime+=5,
  up: ()=> this.video.volume+=0.1,
  down: ()=> this.video.volume-=0.1,
}
document.onkeydown = (event)=> {
  // 左上右下 37 38 39 40
  let key = event.keyCode
  if(key in DIRECTION){
    directions[DIRECTION[key]]()
  }
}
複製程式碼

搞定

視訊進度條

根據currentTime和duration 可以得到視訊的播放事件和總時長

再根據這邊文章Animating URLs with Javascript and Emojis的啟發,用emoji做個圖形進度條

    formatTime(second){
        const m = Math.floor(second/60) + ''
        const s = parseInt(second%60) + ''
        return m.padStart(2,'0')+":"+s.padStart(2,'0')
    }
    showProgress(){
        const current = this.video.currentTime
        const total = this.video.duration
        const per = Math.floor((current/total)*4)
        console.log((current/total).toFixed(2))
        const p = ['?', '?', '?', '?', '?'][per]
        document.title = `${p}${this.formatTime(current)}/${this.formatTime(total)}`
    }
複製程式碼

效果

favicon摸魚工具開發0.2版本

攝像頭

用webrtc實現很簡單,好像沒啥鳥用的功能,純屬娛樂 可以監控身後有沒有老闆 後續考慮看看加個人臉識別,識別老闆 或者自己笑得過於開心,都發個提醒

let video = document.createElement('video')
video.width=this.width
video.autoplay="autoplay"
document.body.appendChild(video)
this.video = video
const mediaStream =  await navigator.mediaDevices.getUserMedia({video:true}) // webrtc
this.video.srcObject = mediaStream

this.video.addEventListener('timeupdate',()=>{
  this.videoToImageByFilter()
},false)
複製程式碼

favicon摸魚工具開發0.2版本

濾鏡

如果你用這個來直播,雖然小了點,但是也可以考慮加一個濾鏡,原理也很簡單,canvas獲取圖片畫素顏色值,對需要的濾鏡,找到公式設定一下就行,比如灰色濾鏡,計算獲取單位元素的RBG然後取平均值 然後複製給自身得到灰色的影像

const context = this.canvas.getContext('2d')
context.clearRect(0, 0, this.SIDE, this.SIDE)
context.drawImage(this.video, 0, 0, this.SIDE, this.SIDE)

//獲取ImageData的屬性:width,height,data(包含 R G B A 四個值);
var imgdata = context.getImageData(0,0,this.SIDE,this.SIDE)

for(var i=0;i<imgdata.data.length;i += 4){
  // 灰色濾鏡
  // 計算獲取單位元素的RBG然後取平均值 然後複製給自身得到灰色的影像
  var avg =  (imgdata.data[i]+ imgdata.data[i+1]+ imgdata.data[i+2])/3
  imgdata.data[i] = imgdata.data[i+1] =imgdata.data[i+2] =avg   
}


this.canvasFilter.getContext('2d').putImageData(imgdata,0,0);
setFavico(this.canvasFilter)
複製程式碼

favicon摸魚工具開發0.2版本

懷舊濾鏡公式

img

程式碼呼之欲出,不要問邏輯,問就是抄公式,很多濾鏡的公式baidu都可以搜到,比如卡通,熔爐,黑白等等

// 懷舊濾鏡
const context = this.canvas.getContext('2d')
context.clearRect(0, 0, this.SIDE, this.SIDE)
context.drawImage(this.video, 0, 0, this.SIDE, this.SIDE)

//獲取ImageData的屬性:width,height,data(包含 R G B A 四個值);
var imgdata = context.getImageData(0,0,this.SIDE,this.SIDE)
for(var i = 0; i < imgdata.height * imgdata.width; i++) {
  var r = imgdata.data[i*4],
      g = imgdata.data[i*4+1],
      b = imgdata.data[i*4+2];

  var newR = (0.393 * r + 0.769 * g + 0.189 * b);
  var newG = (0.349 * r + 0.686 * g + 0.168 * b);
  var newB = (0.272 * r + 0.534 * g + 0.131 * b);
  var rgbArr = [newR, newG, newB].map((e) => {
    return e < 0 ? 0 : e > 255 ? 255 : e;
  });
  [imgdata.data[i*4], imgdata.data[i*4+1], imgdata.data[i*4+2]] = rgbArr;
}
this.canvasFilter.getContext('2d').putImageData(imgdata,0,0);
setFavico(this.canvasFilter)
複製程式碼

favicon摸魚工具開發0.2版本

看到favicon中略顯憂鬱的我,感覺回到了殺馬特的青春歲月,可惜現在髮量不行了

貪食蛇

其實32*32畫素,還是可以實現很多有意思的功能,1px變數後,還有30px可以發揮,基本邊長是2和3畫素的點課件, 我們最多可以有15的邊長可以發揮,比如貪食蛇,俄羅斯方塊,有興趣的可以挑戰坦克大戰,推箱子

話不多說,先寫程式碼,貪食蛇原理就是canvas裡坐遊戲,然後渲染favicon,新建一個類

class Snake {
	constructor(){
        // 15*15
        this.SIDE = 32 // favcion邊長32px
        this.LINE_WIDTH = 1 // 1px
        this.SIZE = 3 // 一個資料點的畫素值
        this.WIDTH =10 // 遊戲空間是10個  (32-2)/3

        this.initCanvas()
    }
	initCanvas(){
		this.canvas = document.createElement('canvas')
        this.canvas.width = this.canvas.height = this.SIDE
    }
	initGrid(){
		this.grid = []
		while(this.grid.length<this.WIDTH){
			this.grid.push(new Array(this.WIDTH).fill(0))
		}
	}
}
複製程式碼

初始化

新建好網格後,我們規定0是初始值,小蛇佔的位置,就是1,食物是2

小蛇初始長度是3,在左側居中,

// 初始化小蛇蛇
initSnake(){
  this.snake = []

  // 初始值長度是3 處置位置在左側中間
  let y = 4
  let x = 0
  let snakeLength = 3
  while(snakeLength>0){
    this.snake.push({x:x,y:y})
    this.grid[y][x] = '1'
    snakeLength--
    x++
  }
  // 小蛇的初始方向是右邊
  this.current = this.directions.right
}
複製程式碼

我們console.table一下this.grid

favicon摸魚工具開發0.2版本
bingo

渲染網格

我們需要把網格渲染到canvas裡 先把canvas放在body裡,方便除錯 方法和很粗暴,只是個玩具,就不考慮渲染的優化了,直接遍歷網格,每次都重新渲染即可


initCanvas(){
	this.canvas = document.createElement('canvas')
    this.canvas.width = this.canvas.height = this.SIDE
    document.body.appendChild(this.canvas)
}

drawCanvas(){
	// 32*32 四周變數1px,所以中間是30*30, 用15*15的格子,每個格子2px
    var context = this.canvas.getContext('2d')  //getContext() 方法可返回一個物件  
    context.clearRect(0,0,this.SIDE,this.SIDE)
    context.strokeStyle = 'green'
    context.lineWidth = this.LINE_WIDTH
  	context.fillStyle = "red"  // 設定或返回用於填充繪畫的顏色、漸變或模式              
	context.strokeRect(0, 0, this.SIDE, this.SIDE)
	
	this.grid.forEach((row,y)=>{
		row.forEach((g,x)=>{
			if(g!==0){
				// 食物或者是蛇
				context.fillRect(this.LINE_WIDTH+x*this.SIZE,this.LINE_WIDTH+y*this.SIZE,this.SIZE,this.SIZE)  // x軸 y軸 寬 和 高 ,繪製“被填充”的矩形  
				
			}
		})
    })
    setFavico(this.canvas)
}
複製程式碼

看下效果

favicon摸魚工具開發0.2版本

小蛇出來了 yeah

放食物

這個沒啥說的,隨機一個座標,然後位置落在小蛇上,就重新隨機,否則就設定成2 畫出來也用紅色 然後直接把canvas截個圖,放在favicon上就可以

// 放吃的
setFood(){
	while(true){
		const x = Math.floor(Math.random()* this.WIDTH)
		const y = Math.floor(Math.random()* this.WIDTH)
		if(this.grid[y][x]=='1'){
			continue
		}else{
			//食物是2
			this.grid[y][x]='2'
			break
		}
	}
}

複製程式碼

favicon摸魚工具開發0.2版本

移動

有上下左右四個方向可以移動,這裡偷個懶 單純的根據方向和蛇頭的位置,判斷下一個蛇頭在的點,然後根據是否吃掉食物,決定尾巴是不是掐掉一個尖即可,比較簡單

  1. 下一個蛇頭在的位置,如果超出網格,或者是小蛇自己,遊戲結束,統計分數放在localstorage裡
  2. setInterval定時移動就可以,根據方向
this.directions = {
	// 左
	'left':{x:-1,y:0},
	// 上
	'up':{x:0,y:-1},
	// 右
	'right':{x:1,y:0},
	// 下
	'down':{x:0,y:1},
}

複製程式碼
move(){	
	// 1. 根據方向,計算出下一個蛇頭所在位置
	// 蛇頭設為
	const head = this.snake[this.snake.length-1]
	const tail = this.snake[0]
	const nextX = head.x+this.current.x
	const nextY = head.y+this.current.y

	// 2. 判斷蛇頭是不是出界  是不是碰見自己個了 如果是屁股尖就碰不到
    const isOut = nextX<0||nextX>=this.WIDTH||nextY<0||nextY>=this.WIDTH
    if(isOut){
        this.initGame()
		return 
    }

	const isSelf = (this.grid[nextY][nextX]) =='1' && !(nextX === tail.x && nextY === tail.y)

	if(isSelf){
        this.initGame()
		return 
	}

    const isFood = this.grid[nextY][nextX]=='2' // 食物是2
    if(!isFood){
        // 所謂蛇向前走,就是把尾巴去掉, 新增nextX和Y
        // 去尾巴 有食物的時候不用去尾
        this.snake.shift()
        this.grid[tail.y][tail.x] = 0
    }else{
        // 食物吃掉了,在放一個
        this.setFood()
        // 加一份
        this.score++
        this.setTitle()
    }

	// 新增頭部
	this.snake.push({x:nextX,y:nextY})
    this.grid[nextY][nextX] = '1'
    this.drawCanvas()
}

複製程式碼

favicon摸魚工具開發0.2版本

後續

基本功能也就這些了,自己摸魚夠用了,這樣就可以開3個tab

  • 1號tab看小視訊
  • 2號tab開攝像頭,防止後面班主任
  • 3號tab玩貪食蛇 其實30 * 30畫素可以做的東西還挺多,比如俄羅斯方塊,魔塔,坦克大戰,都是能放下的,有興趣的可以試試

程式碼地址: github.com/shengxinjin… 過兩天把0.2的程式碼也錄個視訊放B站把,0.1版本視訊歡迎三連

建個群把,一起摸魚,進不去的加我微信 woniu_ppp

image-20200402232718316

相關文章