electron屏幕截图功能

393 阅读2分钟

思路

electron截图工具,其实是通过desktopCapturer捕捉一张全屏的底图放在一个新的窗口上,然后监听鼠标事件记录鼠标点击滑动的区域,在底图上进行裁剪的过程。

创建一个透明的全屏窗口,用来操作截图

import {ipcMain, desktopCapturer, screen , nativeImage, globalShortcut } from 'electron'

//用于保存屏幕底图
let screenImage = '' 

//初始化截图界面
const useScreenshot = () => {
//用于获取屏幕的参数
let screenSize = screen.getPrimaryDisplay().size;

//提前创建窗口防止闪屏
const captureWin = winTool.createWin({
    fullscreen: true,
    transparent: true,
    frame: false,
    resizable: false,
    show: false,
    router:'capture',
    id:'capture',
    alwaysOnTop: true,
})

//在linux中fullscreen可能会失效,所以要设置窗口大小
if(os.platform()==='linux'){
    captureWin.setSize(screenSize.width,screenSize.height)
}
//默认隐藏窗口
captureWin.hide()

//快捷键方式
globalShortcut.register('ctrl+shift+a', () => {
    captureScreen(captureWin)
})

}
//在主进程创建中使用
useScreenshot()

隐藏截图窗口

const closeScreenshot = function(){
    if (captureWin && !captureWin?.isDestroyed()){
            captureWin?.close()
            captureWin = null
            useScreenshot()
    }
}

操作截图的方法

const captureScreen = (captureWin) => {
    //执行等待,怕截图窗口还没加载好
    wait(800)

    let screenSize = screen.getPrimaryDisplay().size;
    const scaleFactor = screen.getPrimaryDisplay().scaleFactor;

    //  //创建一个全屏大小的Buffer
    const { width, height } = screenSize;
    const size = { width: Math.round(width * scaleFactor), height: Math.round(height * scaleFactor) };

    //捕获屏幕截图
    desktopCapturer.getSources({
        types: ['screen'],
        thumbnailSize: size
    }).then(imgs => {

        let imageData=imgs[0].thumbnail.toDataURL()

        screenImage = imageData

        if (captureWin &&  !captureWin.isDestroyed()) {
        captureWin.show();
        captureWin.on('ready-to-show',()=>{
        captureWin.webContents.send("imageData", imageData,scaleFactor)
    })
}

主进程监听用户操作截图事件

ipcMain.on('captureScreen', (e) => {
    captureScreen()	
})

ipcMain.on('closeScreenshot', (e,data:any) => {
    closeScreenshot()
    captureImg(data)
})

将地图裁成截图的屏幕大小,在传给截图的工具页面

function captureImg(data:any){
    const scaleFactor = screen.getPrimaryDisplay().scaleFactor;

    let newData={
        x:Math.round(data.x * scaleFactor),
        y:Math.round(data.y * scaleFactor),
        width:Math.round(data.width * scaleFactor),
        height:Math.round(data.height * scaleFactor),
        img:''
    }

    if(screenImage){
        let img = nativeImage.createFromDataURL(screenImage)
        let image = img.crop({x:newData.x,y:newData.y,width:newData.width,height:newData.height}).toDataURL()

        newData.img = image
        
        //截图工具窗口页面
        if(imgTool && !imgTool.isDestroyed()){
             sendImgTool(newData)
        }else{
             createImgTool(newData)
        }
    
    }
}

渲染进程中的截图界面

<template>
    <div id="box" class="box">
        <div id="div-box" class="div-box"></div>
        <img id="imgBase" :src="data.imageData" draggable="false" />
    </div>
</template>
<script setup>
const ipcRenderer = window.ipcRenderer;
let data = reactive({
    x:0,
    y:0,
    width:0,
    height:0,
    status:false,
    imageData:'',
    scaleFactor:0
})
onMounted(()=>{
    init()
    ipcRenderer.on('imageData',(_,imageData: string,scaleFactor:number)=>{
        data.imageData = imageData
        data.scaleFactor = scaleFactor
    })
})
//移除鼠标事件
onUnmounted(()=>{
    document.removeEventListener('mousedown',handleDown)
    document.removeEventListener('mouseup',handleUp)
    document.removeEventListener('mousemove',handleMove)
})
//监听鼠标事件
const init=()=>{
    document.addEventListener('mousedown',handleDown)
    document.addEventListener('mousemove',handleMove)
    document.addEventListener('mouseup', handleUp)
}
//鼠标按起
const handleUp=(e:any)=>{
    console.log('mouseup',data)
    data.status = false
    if(data.width===window.innerWidth){
        data.x = 0
        data.y = 0
    }
    done()
}
//鼠标移动
const handleMove=(event:any)=>{
    let div = document.getElementById('div-box')
    if(!data.status){
            return
    }
    //这次的宽高
    let width = event.pageX - data.x
    let height = event.pageY - data.y
    
    //如果等于屏幕的高度
    if (
        data.width === width &&
        data.height === height
    ) {
        return;
    }
    //移动的位置小与初始位置
    if(event.offsetX < width || event.offsetY < height){
        return
    }
    //移动的位置=截图的位置
    data.width = width >= 0 ? width : 0;
    data.height = height >= 0 ? height : 0;
    
    if(width>0){
        div.style.width = width + 'px'
    }
    if(height>0){
        div.style.height = height + 'px'
    }

}
//鼠标按下--设置截图的初始位置
const handleDown=(e:any)=>{
    let div = document.getElementById('div-box')
    //是否正在截图
    if (data.status) {
        return
    }
    div.style.width= '0px'
    div.style.height='0px'
    data.x = e.pageX
    data.y = e.pageY
    div.style.left = data.x + 'px'
    div.style.top = data.y + 'px'
    div.style.position = 'absolute'
    div.style.display='block'

    data.status = true
}
//裁剪截图大小
const done = ()=>{
    let params = {}
    //图片的宽度
    if(data.width===0){
        params = {
            x:0,
            y:0,
            width:window.innerWidth,
            height:window.innerHeight
        }
    }else{
        params = {
            x:Math.round(data.x)+4,
            y:Math.round(data.y)+4,
            width:data.width-4,
            height:data.height-4
        }
    }
    //截图结束,把截图的大小传到主进程
    ipcRenderer.send('closeScreenshot', params)
}

</script>
<style scoped lang="scss">
* {
  margin: 0;
  padding: 0;
  user-select: none;
  -webkit-user-drag: none;
}
.box{
    cursor: crosshair;
    position: fixed;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
}
.div-box{
    position: absolute;
    width: 0px;
    height: 0px;
    left: 0;
    top:0;
    box-shadow: 0 0 0 20000px rgba(0, 0, 0, 0.4);
    justify-content: center;
    flex-direction: column;
    align-items: center;
    display: none;
    border:1px solid #fff;
    // transition: all 0.1s;
}
#imgBase {
  position: absolute;
  width: 100%;
  z-index: -1;
  image-rendering: -webkit-optimize-contrast;
}
</style>