首页 > 其他分享 >electron + vue3 自定义窗口:移动,缩放,置顶

electron + vue3 自定义窗口:移动,缩放,置顶

时间:2024-06-11 19:00:21浏览次数:27  
标签:... const 自定义 缩放 win handleWin ._ maxStatus 置顶

electron

main.js

const { BrowserWindow , ipcMain } = require('electron');
const path = require("path")
const CustomWindow = require('./CustomWindow')

const win = new BrowserWindow({
    frame: false,
    transparent: true,
    center: true,
    webPreferences: {
        preload: path.join(__dirname, `./preload.js`)
    }
})

const customWin = new CustomWindow(win)
ipcMain.on('on-custom-win', (_, funName, ...arg) => customWin[funName](...arg));

preload.js

const { contextBridge, ipcRenderer } = require('electron');
const handleWin = {
    changeSize: (...args) => ipcRenderer.send('on-custom-win', 'autoMax', ...args),
    moveStart: () => ipcRenderer.send('on-custom-win', 'moveStart'),
    moveEnd: () => ipcRenderer.send('on-custom-win', 'moveEnd'),
    top: () => ipcRenderer.send('on-custom-win', 'top'),
    maximize: () => ipcRenderer.send('on-custom-win', 'maximize'),
    onStatus: (fun) => ipcRenderer.on('window-status', fun)
};
contextBridge.exposeInMainWorld('ipcApi', {
    handleWin 
});

CustomWindow.js

const { screen } = require('electron');
/**
 * 窗口移动|缩放|置顶
 */
module.exports = class {
    constructor(win) {
        this.isMove = false;
        this.startPosition = [0, 0]
        this.oldPosition = [0, 0];
        this.maxStatus = false;
        this.topStatus = false;
        this.nowSize = null;
        this.nowPosition = null;
        this._initialization(win);
    };
    _initialization(win) {
        this.win = win;
        this.defaultSize = win.getSize();
        this.defaultPosition = win.getPosition();
        if (win.isAlwaysOnTop()) this.top();
        this.autoMax(...this.defaultSize);
    };
    _isWin = () => this.win && !this.win.isDestroyed();
    moveStart() {
        this.isMove = true;
        if (!this._isWin()) return this.moveEnd();
        // 获取鼠标按下位置
        let { x, y } = screen.getCursorScreenPoint();
        const winPosition = this.win.getPosition();
        x -= winPosition[0];
        y -= winPosition[1];
        this.startPosition = [x, y];
        this._moveing();
    };
    _moveing() {
        if (!this.isMove) return;
        let { x, y } = screen.getCursorScreenPoint();
        x -= this.startPosition[0];
        y -= this.startPosition[1];
        if (x !== this.oldPosition[0] || y !== this.oldPosition[1]) {
            this._restoreSize();
            this.win.setPosition(x, y);
            this.oldPosition = [x, y];
        };
        setTimeout(() => this._moveing(), 16);
    };
    moveEnd = () => this.isMove = false;
    _restoreSize() {
        if (!this.maxStatus || !this.oldPosition[0] || !this.oldPosition[1]) return;
        let startPositionX = parseInt(this.nowSize[0] / 2);
        const { x: leftBoundary, width: screenWidth } = this._getNowScreenSize();
        const rightBoundary = leftBoundary + screenWidth - this.nowSize[0];
        let { x: movePosition } = screen.getCursorScreenPoint();
        movePosition -= startPositionX;
        if (movePosition < leftBoundary) {
            startPositionX += movePosition - leftBoundary;
        };
        if (movePosition > rightBoundary) {
            startPositionX += movePosition - rightBoundary;
        };
        this.startPosition[0] = startPositionX;
        this.win.setSize(...this.nowSize);
        this.maxStatus = false;
        this._sendStatus('max', false);
    };
    _getNowScreenSize() {
        let { x, y } = this.win.getBounds();
        if (x < 0) x = 0;
        for (const display of screen.getAllDisplays()) {
            if (x >= display.bounds.x && x < display.bounds.x + display.bounds.width &&
                y >= display.bounds.y && y < display.bounds.y + display.bounds.height) {
                return display.bounds;
            };
        };
    };
    top() {
        if (!this._isWin()) return;
        this.topStatus = !this.topStatus;
        this.win.setAlwaysOnTop(this.topStatus);
        this._sendStatus('top', this.topStatus);
    };
    autoMax(width, height) {
        if (!this._isWin() || !this.win.isResizable() || this.maxStatus) return;
        const { width: screenWidth, height: screenHeight } = this._getNowScreenSize();
        if (screenWidth - width < 100 && screenHeight - height < 100) {
            this.nowSize = this.defaultSize;
            this.nowPosition = this.defaultPosition;
            this.maxStatus = true;
            this._sendStatus('max', true);
            this.win.maximize();
            this.oldPosition = [0, 0];
        };
    };
    maximize() {
        if (!this._isWin() || !this.win.isResizable()) return;
        this.maxStatus = !this.maxStatus;
        if (this.maxStatus) {
            this.nowSize = this.win.getSize();
            this.nowPosition = this.win.getPosition();
            this.win.maximize();
        } else {
            this.win.setSize(...this.nowSize);
            this.win.setPosition(...this.nowPosition);
        };
        this.oldPosition = [0, 0];
        this._sendStatus('max', this.maxStatus);
    };
    _sendStatus = (type, value) => this.win.webContents.send('window-status', { type, value });
}

VUE

main.js 注册指令

import { createApp } from 'vue'
import App from '@/App.vue'
import registerDirectives from '@/directives'
const app = createApp(App)
registerDirectives(app)
app.mount('#app')

directives 自定义指令

index.js 

import interactWin from './interactWin';
export default function registerDirectives(app) {
    app.directive("handle-win", { mounted: (...arg) => interactWin.handle(...arg) })
}

interactWin.js


const { handleWin } = window.ipcApi;
class InteractWin {
    constructor() {
        this.el = null;
    };
    handle(el, binding = null) {
        if (this.el) return;
        this.el = el;
        this.el.addEventListener('mousedown', this._mouseDown);
        this.el.addEventListener('dblclick', this._mouseDblclick);
        window.addEventListener('resize', this._handleResize);
        if (binding?.value && typeof binding.value === 'function') {
            handleWin.onStatus((_, ...arg) => binding.value(...arg));
        };
    };
    _mouseDblclick = () => handleWin.maximize();
    _mouseDown = (e) => {
        if (e.button !== 0) return;
        handleWin.moveStart();
        window.addEventListener('mouseup', this._globalMouseUp);
    };
    _globalMouseUp = (e) => {
        if (e.button !== 0) return;
        handleWin.moveEnd();
        window.removeEventListener('mouseup', this._globalMouseUp);
    };
    //此处加防抖可提升性能
    _handleResize = () => {
        handleWin.changeSize(window.innerWidth, window.innerHeight);
    };
};
export default new InteractWin();

vue使用指令操作窗口

在头部DOM上使用 v-handle-win;

1:左键移动窗口;

2:双击最大化窗口与还原窗口;

3:最大化移动窗口自动还原窗口;

4:手动改变窗口尺寸至屏幕最大范围(范围阈值在CustomWindow.js内修改)时自动最大化;

<template>
    <div v-handle-win="onWinHandle">
       <!-- 自定义窗口头部,使用指令并监听使用反馈 -->
    </div>
    <div @click="winTop">
       是否置顶:{{ topStatus }}
    </div>
    <div @click="winMax">
       是否最大化:{{ maxStatus }}
    </div>
</template>
<script setup>
import { ref } from 'vue';
const { handleWin } = window.ipcApi;
const maxStatus = ref(false)
const topStatus = ref(false)
/**
 * 监听操作窗口头部的反馈
 */
const onWinHandle = ({ type, value }) => {
    switch (type) {
        case 'max':
            maxStatus.value = value
            break;
        case 'top':
            topStatus.value = value
            break;
    }
}
/**
 * 窗口置顶
 */
const winTop = () => handleWin.top();
/**
 * 最大化与还原
 */
const winMax = () => handleWin.maximize();

标签:...,const,自定义,缩放,win,handleWin,._,maxStatus,置顶
From: https://blog.csdn.net/qq_19838177/article/details/139605318

相关文章

  • 利用自定义标签,实现select下拉列表默认选中
    //创建块函数方法,用于替换文本中的值functionsmarty_block_get_cates($params,$content,&$_sm,&$repeat){if(!$repeat){$ci=&get_instance();$cates=$ci->db->get('category')->result_array();$cates=get_dat......
  • WPF阻止窗体被系统缩放,使用显示器DPI
    WPF默认是跟随系统DPI变化(缩放与布局)而缩放窗体的;微软把它称为默认DPI感知,当DPI发生变化时WPF感知到后缩放窗体,介绍链接:设置进程的默认DPI感知(Windows)-Win32apps|MicrosoftLearn如果我们不希望窗体被缩放,而是让窗体使用显示器DPI该怎么办呢?首先修改app.manifest,如......
  • python绘制词云图最全教程,查看文章关键词,自定义词云图形状等,看完就会
    ......
  • VsCode中snippets --- vue自定义代码片段
    vue自定义代码片段Vue2代码片段1、点击文件→首选项→选择配置用户代码片段2、在弹出这个窗口中选择新建全局代码片段文件3、选择后在此处输入文件名后按‘Enter’键确定4、点击确定后会生成以下文件5、替换成以下vue2代码片段6、使用代码片段Vue3代码片段使用defineC......
  • 苹果iOS 18发布:新增锁屏自定义和应用锁
    今天凌晨1点,iOS18在苹果WWDC24上正式发布。全新的iOS18允许用户自由定义App排列,可以自由选择App颜色主题,并且iOS18升级支持锁屏状态自定义功能,还支持单个App的应用锁,保护用户隐私。与此同时,iOS18对控制中心也进行了升级调整,全新的控制中心更具有扩展性,支持第三方应用控制按......
  • SpringSecurity如何自定义用户认证逻辑?
    在SpringSecurity中自定义用户认证逻辑通常涉及到实现你自己的UserDetailsService或使用自定义的AuthenticationProvider。下面是通过这两种方式自定义用户认证逻辑的基本演示:使用UserDetailsService自定义UserDetailsService是SpringSecurity用于从数据库、L......
  • 自定义注解获取属性对应枚举的翻译值
    平时在开发的时候难免会遇到枚举来翻译类,于是写一个自定义注解来在开发的时候自动翻译枚举的值相关代码如下:@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@Documented@JacksonAnnotationsInsidepublic@interfaceEnumShow{/***要转换......
  • python实现自定义线程池
    线程池ThreadPool对象的几个关键方法:get_idle_num():获得当前空闲线程的数量submit(task:callable):把一个任务(实际上就是一个函数)提交到线程池中执行.如果没有空闲线程则阻塞.wait_idle():阻塞,直到有空闲线程stop():停止线程池中的所有线程.(注意:非强制停止,......
  • 【置顶】博客索引
    主要将博客按照不同版块分了个类,方便查找。算法学习笔记:反悔贪心学习笔记游记:CodeforcesRound923(Div.3)比赛记录台州市赛游记(初赛)初赛:C++中符号的优先级Linux常用命令台州市赛游记(初赛)......
  • 自定义类型:结构体
    目录1.结构体类型的声明1.1结构体1.1.1结构体如何声明1.1.2结构体变量的创建和初始化1.2结构的特殊声明1.3结构的自引用2.结构体内存对齐2.1对齐规则2.2为什么存在内存对齐?2.3修改默认对齐数3.结构体传参4.结构体实现位段 4.1什么是位段4.2位段的......