js实现多屏/跨屏窗口放置

目前有越来越多的开发者外接多个显示器进行工作,但是有没有发现你在浏览器中打开新窗口时,都只会显示在你当前的显示屏里,如果想放置在某个浏览器中,需要把窗口拖过去,有没有可能直接在指定的显示屏中打开窗口呢?

通常我们可以通过 window.open 方式来打开一个窗口,如:

const popup = window.open(
  "https://www.baidu.com/",
  "_blank",
  "width=400, height=300, left=100, top=100"
);

但是都只是在当前屏幕上打开的,如何能让其在指定的显示屏中打开窗口呢?

01

Multi-Screen Window Placement API

从Chrome 86起,开始支持Multi-Screen Window Placement API了,不过还处于试验阶段,可以在 chrome://flags 里开启 #enable-experimental-web-platform-features

 判断浏览器是否支持该特性

if ("getScreens" in window) {
  //支持
}

判断是否接入多个显示屏

await isMultiScreen()

请求权限

const permission = await navigator.permissions.query({
    name: 'window-placement'
});

getScreens()方法

await window.getScreens();

事件监听

window.addEventListener('screenschange', () => {
    //
});

全屏

requestFullscreen也相应增加了screen配置项

***.requestFullscreen({
    screen: screen[0]
})

02

演示效果

下面视频演示分别点击页面上的3个按钮,会分别在对应的显示屏中打开一个小窗口。

03

代码示例

css、html

<style>
#demo{width: 600px;}
#demo .warning {font-size: 14px; color: #f00}
#demo .buttons input[type="button"] {height: 40px; font-size: 15px; margin-right: 20px; color: #fff; background: #389e88; border: 0; border-radius: 5px;}
#demo .buttons input[type="button"]:active {background: rgb(10, 233, 10)}
#demo .screens {margin-top: 10px}
#demo .screens dl {background: #ccc; font-size: 13px; line-height: 20px; margin-top: 10px; padding: 10px}
#demo .screens dt {font-size: 16px; font-weight: bold; color: #1d3bd2; margin-bottom: 10px}
#demo .screens dd {padding: 0; margin: 0;}
</style>


<div id="demo">
    <strong class="warning"></strong>
    <div class="buttons"></div>
    <div class="screens"></div>
</div>

js

class MultiScreen {
    constructor() {
        const _this = this;
        _this.screens = [];
        _this.init();
    }
    async init() {
        const _this = this;
        if (!('getScreens' in self)) {
            alert('你的chrome版本不支持该功能!');
        } else if ('isMultiScreen' in self && !(await isMultiScreen())) {
            alert('请接入多个显示屏查看该demo!');
        } else {
            const permission = await navigator.permissions.query({
                name: 'window-placement'
            });
            permission.addEventListener('change', () => {
                _this.updateScreensInfo();
            });
            if ('onscreenschange' in self) {
                window.addEventListener('screenschange', () => {
                    _this.updateScreensInfo();
                });
                _this.updateScreensInfo();
            }
        }
    }
    async getScreensData() {
        const _this = this;
        _this.screens = (await getScreens().catch(_ => {})) || [window.screen];
    }
    async updateScreensInfo() {
        const _this = this;
        let buttons = [];
        let screens = [];


        await _this.getScreensData();


        const permission = await navigator.permissions.query({
            name: 'window-placement'
        });


        if (permission.state === 'denied') {
            document.querySelector('#demo .warning').innerHTML = '您禁用了窗口放置权限,请开启使用';
        }


        _this.screens.forEach((item, index) => {
            buttons.push(`
                <input type="button" value="在第 ${index + 1} 个显示屏中打开" data-index="${index + 1}">
            `);
            screens.push(`
                <dl>
                    <dt>screen ${index + 1} :</dt>
                    <dd>
                        id: ${item.id}<br>
                        width: ${item.width}<br>
                        height: ${item.height}<br>
                        availWidth: ${item.availWidth}<br>
                        availHeight: ${item.availHeight}<br>
                        left: ${item.left}<br>
                        top: ${item.top}<br>
                        primary: ${item.primary}<br>
                        internal: ${item.internal}<br>
                        touchSupport: ${item.touchSupport}<br>
                    </dd>
                </dl>
            `);
        });
        document.querySelector('#demo .buttons').innerHTML = buttons.join('');
        document.querySelector('#demo .screens').innerHTML = screens.join('');
        document.querySelectorAll('#demo input[type="button"]').forEach((item, index) => {
            item.addEventListener('click', () => _this.openWin(index, screens[index]));
        });
    }
    openWin(index, html) {
        const _this = this;
        const screen = _this.screens[index];
        const optionsStr = `
            width=400,
            height=300,
            left=${screen.availLeft + (screen.availWidth / 2) - 200},
            top=${screen.availTop + (screen.availHeight / 2) - 150}
        `;
        const win = window.open('about:blank', '_blank', optionsStr);
        win.document.write(html);
    }
}
new MultiScreen();

04

screen数据

以下是部分screen数据:

其中 primary 表示是否是主显示屏,internal 表示是否是内置显示屏。

id: 0
width: 1280
height: 800
availWidth: 1280
availHeight: 734
left: 0
top: 0
primary: true
internal: true
touchSupport: false
id: 1
width: 1920
height: 1200
availWidth: 1920
availHeight: 1177
left: 0
top: -1200
primary: false
internal: false
touchSupport: false

05

应用场景

可能有人会觉得这个功能意义不大,但是我觉得一方面可以提升交互体验,另一方面,随着多屏、折叠屏等设备越来越多,跨屏交互势必会被重视,(如折叠屏就已经有了相关css与js api: @media (spanning: single-fold-vertical), window.getWindowSegments()),可以提前掌握并进行一些技术创新。

目前我能想到的几个应用场景:

1、股市行情展示

同时在多个屏幕上打开并排列不同的股市行情窗口。

2、数据可视化大屏展示

目前的数据可视化大屏展示基本都是整合在一个页面中的,如果需要调整结构布局,可能就需要修改代码,如果开发时考虑好让每个模块可以独立展示,那么就可以借助这个功能,设定好布局后,就可以一键窗口排列展示。

3、在线ppt演示

目前市场上有很多在线版的ppt,提供了演讲者模式,一个窗口是用来在投影仪上展示的,另一个是用来在笔记本屏幕上显示演讲者笔记,但是目前使用时就需要手动去调整窗口到不同的屏幕上,现在我们是否可以这样去优化提升体验,点击 “演讲者模式” 按钮后,直接一个窗口全屏展示在投影仪,另一个演讲者笔记窗口保留在笔记本屏幕上。

4、简单的小游戏多屏布局

...

06

一点设想

目前要在不同屏幕上打开窗口,主要还是通过 js 的window.open去实现,以后能不能直接在 <a> 标签的_target上增加一项呢,指定在哪个显示屏上打开,如:

<a href="https://www.so.com/" _target="screen[0]">...</a>

以及 window.open 第二个参数是不是也可以考虑支持呢?

07

Chrome扩展

在此顺便推广一下我以前开发的一个chrome扩展 “多显示窗口管理” :

详细介绍见另一个文章:Chrome插件分享:多显示器窗口管理

该扩展支持在多个显示器之间进行窗口切换、最大化、居中、贴边、垂直/水平排列、标签页从窗口分离/合并、以及快捷键支持。

1. 窗口可以在多个显示器之间快速移动,而无需拖动;

2. 窗口最大化、居中、贴边;

3. 窗口1x2、2x1、2x2排列;

4. 窗口标签页从窗口分离、合并;

默认支持以下快捷键:

1. 在多个显示器之间切换(windows: ctrl+→,Mac: ⌘+→)

2. 调整窗口大小且循环切换(windows: ctrl+↑,Mac: ⌘+↑)

3. 窗口依次在屏幕左、上、右、下贴边切换(windows: ctrl+←,Mac: ⌘+←)

其他快捷键可自定义设置。

end

js实现多屏/跨屏窗口放置
标签: