| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- <template>
- <div class="box">
- <div id="menu">
- <span class="submenu">
- <span>{{ status }}</span> SOCKET
- </span>
- <span class="submenu">
- <span>
- <span
- v-for="(item, index) in colors"
- :key="index"
- @click="restColor(index)"
- >{{ item }}</span>
- </span>
- <span>
- <a
- href="https://beian.miit.gov.cn/"
- target="_blank"
- rel="noopener noreferrer"
- >蜀ICP备2021004869号-1</a>
- </span>
- </span>
- </div>
- <div id="cursors" />
- <canvas
- id="canvas"
- :style="resetStyle"
- />
- </div>
- </template>
- <script lang="ts" setup>
- import { Painter } from './js/Painter.js'
- import { Recorder } from './js/Recorder.js'
- import {
- onMounted, ref, nextTick, onUnmounted
- } from 'vue'
- import { io } from 'socket.io-client'
- const painters: any = {}
- let recorder: any = null
- let ptime = 0
- const status = ref('🟡')
- const colors = [ '⚫️', '🔵', '🟢', '🟣', '🔴', '🟤', '🟠', '🟡', '⚪️' ]
- let canvas: HTMLCanvasElement | null = null
- let context: CanvasRenderingContext2D | null = null
- const resetStyle = ref('')
- // canvas fn
- const onPointerMove = (event: PointerEvent) => {
- if (event.isPrimary) {
- // Throttle to 20Hz
- const throttle = 1000 / 20
- const time = event.timeStamp
- if (time < (ptime + throttle)) return
- ptime = time
- //
- const x = Math.floor(event.pageX * window.devicePixelRatio)
- const y = Math.floor(event.pageY * window.devicePixelRatio)
- recorder.move(x, y)
- }
- }
- const onPointerDown = (e: PointerEvent) => {
- if (e.isPrimary && e.button === 0 && canvas) {
- const x = Math.floor(e.pageX * window.devicePixelRatio)
- const y = Math.floor(e.pageY * window.devicePixelRatio)
- recorder.down(x, y)
- canvas.addEventListener('pointermove', onPointerMove)
- }
- }
- const onPointerUp = (event: PointerEvent) => {
- if (event.isPrimary && event.button === 0) {
- recorder.up()
- canvas?.removeEventListener('pointermove', onPointerMove)
- }
- }
- const restColor = (i: number) => {
- recorder.color(i)
- const w = canvas?.width || 0
- const h = canvas?.height || 0
- const r = window.devicePixelRatio
- resetStyle.value = `width:${w / r}px;height:${h / r}px; cursor: url(/pen/${i}.cur) 0 17, crosshair;`
- }
- // socket init
- const socket = io(import.meta.env.VITE_SOCKET_URL, { autoConnect: false, auth: { roomID: 'drawCanvas', name: 'test' }, transports: [ 'websocket' ] })
- socket.on('connect', () => {
- recorder = new Recorder(context, socket)
- canvas?.addEventListener('pointerdown', onPointerDown)
- canvas?.addEventListener('pointerup', onPointerUp)
- status.value = colors[2]
- })
- socket.on('msg', (data: any) => {
- const dataview = new DataView(data)
- const id = dataview.getUint8(0)
- if (painters[id] === undefined) {
- const container = document.getElementById('cursors')
- painters[id] = new Painter(context, container)
- }
- painters[id].execute(dataview)
- })
- socket.on('connect_error', () => {
- status.value = colors[4]
- })
- // init canvas
- const intCanvas = () => {
- canvas = document.getElementById('canvas') as HTMLCanvasElement
- canvas.width = window.innerWidth * window.devicePixelRatio
- canvas.height = window.innerHeight * window.devicePixelRatio
- canvas.style.width = `${window.innerWidth}px`
- canvas.style.height = `${window.innerHeight}px`
- context = canvas.getContext('2d') as CanvasRenderingContext2D
- context.lineWidth = 0.7 * window.devicePixelRatio
- context.fillStyle = 'rgb(238, 238, 238)'
- context.fillRect(0, 0, canvas.width, canvas.height)
- }
- onMounted(() => {
- document.title = '多人在线画板'
- intCanvas()
- nextTick(() => {
- socket.connect()
- })
- window.addEventListener('resize', intCanvas)
- })
- onUnmounted(() => {
- if (!socket) return
- socket.disconnect()
- window.removeEventListener('resize', intCanvas)
- context = null
- canvas = null
- recorder = null
- })
- </script>
- <style lang="scss" scoped>
- .box {
- background: #eeeeee;
- font-size: 12px;
- font-family: sans-serif;
- margin: 0;
- overflow: hidden;
- #canvas {
- display: block;
- cursor: url('/pen/0.cur') 0 17, crosshair;
- touch-action: none;
- }
- #cursors {
- position: absolute;
- width: 100%;
- height: 100%;
- overflow: hidden;
- pointer-events: none;
- }
- #menu {
- position: absolute;
- bottom: 10px;
- width: 100%;
- text-align: center;
- user-select: none;
- pointer-events: none;
- }
- span.submenu {
- display: inline-block;
- background-color: #ffffff;
- color: #999999;
- padding: 6px 12px;
- border-radius: 25px;
- user-select: none;
- pointer-events: auto;
- &:last-child {
- margin-left: 5px;
- }
- &>span {
- span {
- margin: 0 3px;
- cursor: grabbing;
- }
- }
- }
- }
- </style>
|