canvas实现截图功能

代码地址

https://github.com/aminwangaa/screenshot.git

效果图

功能实现:截取左侧的图片内容 填满右侧的区域

在这里插入图片描述
在这里插入图片描述

思路

选择框的四个角是由css实现的所以不是通过每一个角的click事件处理
判断鼠标按下时的相对位置: 左上、左下、右上、右下、中间
盒子最小宽高设置为100 * 100
图片像素为400 * 400
canvas画布大小为200 * 200
为了图片显示不模糊 canvas标签的宽高设置成400 在css中又设置为200
代码方面是边写边算的,没有规划,看着比较乱

import React from 'react';
import catImg from "./img/cat.jpg"
import "./screenshot.less"

const LEFT_TOP = "leftTop"
const RIGHT_TOP = "rightTOp"
const LEFT_BOTTOM = "leftBottom"
const RIGHT_BOTTOM = "rightBottom"
const LEFT_CENTER = "leftCenter"

class Screenshot extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            ctx: null,
            canvas: null,
            start: false,
            borderWidth: 32,
            minWidth: 100,
            minHeight: 100,
            canvasWidth: 400,
            canvasHeight: 400
        }
        this.canvasRef = React.createRef()
        this.imgRef = React.createRef()
        this.maskRef = React.createRef()
        this.imgBoxRef = React.createRef()
    }

    componentDidMount = async () => {
        this.setCanvas()
    }

    setCanvas = async () => {
        const canvas = this.canvasRef.current
        const ctx = canvas.getContext("2d")
        await this.setState({ ctx, canvas })
    }

    onMouseDown = (e) => {
        e.preventDefault()
        e.persist()
        const { start, borderWidth } = this.state
        const imgBox = this.imgBoxRef.current
        if (!start) {
            const startX = e.clientX
            const startY = e.clientY
            const mask = this.maskRef.current
            const startW = mask.clientWidth
            const startH = mask.clientHeight
            let direction = null

            const diffX = startX - imgBox.offsetLeft - mask.offsetLeft
            const diffY = startY - imgBox.offsetTop - mask.offsetTop

            if (
                diffX > borderWidth &&
                diffY > borderWidth &&
                diffX < mask.clientWidth - borderWidth &&
                diffY < mask.clientHeight - borderWidth
            ) {
                direction = LEFT_CENTER
            } else if (diffX <= borderWidth && diffY <= borderWidth) {
                direction = LEFT_TOP
            } else if (diffX >= mask.clientWidth - borderWidth && diffY <= borderWidth) {
                direction = RIGHT_TOP
            } else if (diffX <= borderWidth && diffY >= borderWidth) {
                direction = LEFT_BOTTOM
            } else if (
                diffX >= mask.clientWidth - borderWidth &&
                diffY >= mask.clientHeight - borderWidth
            ) {
                direction = RIGHT_BOTTOM
            }

            this.setState({
                start: true,
                startX,
                startY,
                startW,
                startH,
                direction,
                startT: mask.offsetTop,
                startL: mask.offsetLeft
            })
        }
    }

    onMouseMove = async (e) => {
        e.preventDefault()
        const {
            start,
            startX,
            startY,
            direction,
            startW,
            startH,
            startT,
            startL,
            minWidth,
            minHeight
        } = this.state
        const mask = this.maskRef.current
        const img = this.imgRef.current

        if (start) {
            let moveX =  e.clientX - startX
            let moveY = e.clientY - startY
            if (direction === LEFT_TOP) {
                const dLeftTopWidth = startW - moveX
                const dLeftTopHeight = startH - moveY
                const dLeft = startL + moveX
                if (dLeftTopWidth < minWidth || dLeftTopHeight < minHeight) return
                if ((mask.offsetLeft + mask.clientWidth) > img.clientWidth) return
                const dTop = startT + moveY
                mask.style.top = dTop <= 0 ? 0 : dTop  + "px"
                mask.style.left = dLeft <= 0 ? 0 : dLeft + "px"

                mask.style.width = dLeftTopWidth < minWidth ? minWidth : dLeftTopWidth + "px"
                mask.style.height = dLeftTopHeight < minHeight ? minHeight : dLeftTopHeight + "px"
            }
            if (direction === LEFT_BOTTOM) {
                const dLeft = startL + moveX
                const dLeftBottomWidth = startW - moveX
                const dLeftBottomHeight = startH + moveY
                if (dLeftBottomWidth < minWidth || dLeftBottomHeight < minHeight) return
                if ((mask.offsetTop + mask.clientHeight) > img.clientHeight) return
                mask.style.left = dLeft <= 0 ? 0 : dLeft + "px"
                mask.style.width = dLeftBottomWidth < minWidth ? minWidth : dLeftBottomWidth + "px"
                mask.style.height = dLeftBottomHeight < minHeight ? minHeight : dLeftBottomHeight + "px"
            }
            if (direction === RIGHT_BOTTOM) {
                const dRightBottomWidth = startW + moveX
                const dRightBottomHeight = startH + moveY
                if (dRightBottomWidth < minWidth || dRightBottomHeight < minHeight) return
                if ((mask.offsetTop + mask.clientHeight) > img.clientHeight) return
                if ((mask.offsetLeft + mask.clientWidth) > img.clientWidth) return
                mask.style.width = dRightBottomWidth < minWidth ? minWidth : dRightBottomWidth + "px"
                mask.style.height = dRightBottomHeight < minHeight ? minHeight : dRightBottomHeight + "px"
            }
            if (direction === RIGHT_TOP) {
                const dTop = startT + moveY
                const dRightTopWidth = startW + moveX
                const dRightTopHeight = startH - moveY
                if (dRightTopWidth < minWidth || dRightTopHeight < minHeight) return
                if ((mask.offsetLeft + mask.clientWidth) > img.clientWidth) return
                mask.style.top = dTop <= 0 ? 0 : dTop + "px"
                mask.style.width = dRightTopWidth < minWidth ? minWidth : dRightTopWidth + "px"
                mask.style.height = dRightTopHeight < minHeight ? minHeight : dRightTopHeight + "px"
            }

            if (direction === LEFT_CENTER) {
                // 中间移动的
                const dLeft = startL + moveX
                const dTop = startT + moveY
                const mTop = img.clientHeight - mask.clientHeight
                const mLeft = img.clientWidth - mask.clientWidth
                const tTop = dTop > mTop
                const tLeft = dLeft > mLeft

                mask.style.top = (dTop <= 0 ? 0 : tTop ? mTop : dTop) + "px"
                mask.style.left = dLeft <= 0 ? 0 : tLeft ? mLeft : dLeft + "px"
            }
        }
    }

    onMouseUp = async (e) => {
        e.preventDefault()
        const mask = this.maskRef.current
        this.setState({
            start: false,
            startT: mask.offsetTop,
            startL: mask.offsetLeft
        })
        this.setCanvasImg()
    }

    onMouseLeave = async (e) => {
        e.preventDefault()
        const mask = this.maskRef.current
        this.setState({
            start: false,
            startT: mask.offsetTop,
            startL: mask.offsetLeft
        })
        this.setCanvasImg()
    }

    setCanvasImg = async () => {
        const { ctx, canvas } = this.state
        const { onChange } = this.props
        const mask = this.maskRef.current
        const img = this.imgRef.current
        ctx.clearRect(0,0, canvas.width, canvas.height);
        ctx.drawImage(
            img,
            mask.offsetLeft * 2,
            mask.offsetTop * 2,
            mask.clientWidth * 2,
            mask.clientHeight * 2,
            0, 0, canvas.width, canvas.height)
        // toDataURL(type, quality) 参数可选
        // type 默认为 image/png
        // quality 0-1 默认为 0.92
        const dataURL = canvas.toDataURL("image/jpeg", 1.0);
        onChange && onChange(dataURL)
        await this.setState({ ctx, dataURL })
    }

    render () {
        const { canvasWidth, canvasHeight} = this.state
        return (
            <div className={"screenshotBox"}>
                <div
                    className={"imgBox"}
                    ref={this.imgBoxRef}
                >
                    <img
                        id={"img"}
                        src={catImg}
                        alt=""
                        className={"img"}
                        ref={this.imgRef}
                        onMouseMove={(e) => e.preventDefault()}
                        onMouseDown={(e) => e.preventDefault()}
                        onMouseUp={this.onMouseUp}
                    />
                    <div
                        ref={this.maskRef}
                        className={"mask"}
                        onMouseDown={this.onMouseDown}
                        onMouseLeave={this.onMouseLeave}
                        onMouseMove={this.onMouseMove}
                        onMouseUp={this.onMouseUp}
                    />
                </div>
                <div className={"imgBox"}>
                    <canvas
                        width={canvasWidth}
                        height={canvasHeight}
                        id={"canvas"}
                        ref={this.canvasRef}
                    />
                </div>
            </div>
        )
    }
}

export default Screenshot;
.screenshotBox {
  position: relative;
  height: 210px;
  width: 600px;
  padding-top: 48px;
  display: flex;
  justify-content: space-evenly;
}

.imgBox {
  position: relative;
  display: inline-block;
  margin-top: 1px;
  width: 202px;
  height: 202px;
  border: 1px solid #efefef;
  overflow: hidden;
}

.img {
  position: relative;
  display: inline-block;
  width: 200px;
  height: 200px;
  box-sizing: border-box;
  user-select: none;
  user-focus: none;
}

#canvas {
  width: 200px;
  height: 200px;
}

.mask {
  position: absolute;
  height: 200px;
  width: 200px;
  max-width: 200px;
  max-height: 200px;
  top: 0;
  left: 0;
  background: linear-gradient(#d9d9d9, #d9d9d9) left top,
  linear-gradient(#d9d9d9, #d9d9d9) left top,
  linear-gradient(#d9d9d9, #d9d9d9) right top,
  linear-gradient(#d9d9d9, #d9d9d9) right top,
  linear-gradient(#d9d9d9, #d9d9d9) left bottom,
  linear-gradient(#d9d9d9, #d9d9d9) left bottom,
  linear-gradient(#d9d9d9, #d9d9d9) right bottom,
  linear-gradient(#d9d9d9, #d9d9d9) right bottom;
  background-repeat: no-repeat;
  background-size: 3px 24px, 24px 3px;
  cursor: grab;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值