搭建环境
1、安装node.js
2、npm install electron -g
3、验证:electron -v
升级:npm update electron -g
卸载:npm uninstall electron
第一个electron程序
至少需要三个文件
1、package.json:用于配置electron工程(可以通过npm init 创建)
package.json {
'name':'first',
'version':'1.0.0.0',
'main':'index.js'
}
2、index.js:相当于electron的入口点
const { app,BrowserWindow}=require('electron')
function createWindow(){
win=new BrowserWindow({width:800,height:600});
win.loadFile('index.html');
win.on('closed',()=>{
Win=null;
})
}
app.on(‘ready’,createWindow);
3、Index.html:主窗口的UI页面文件
4、启动 electron [appName]
窗口
窗口尺寸位置
BrowserWindow类创建创建时可以指定
width,height,minWidth,minHeight,maxWidth,maxHeight,x,y(x,y是起始位置)
全屏窗口
BrowserWindow类创建创建时指定{fullscreen:true}即可
BrowserWindow的setFullScreen方法可以动态将窗口设置为全屏(但fullscreenable属性必须是true)
BrowserWindow的isFullScreen()方法可以获取窗口是否为全屏
无边框、透明窗口
BrowserWindow类创建创建时指定{frame:false,transparent:true} 。frame:false表示无边框,transparent:true表示透明
窗口锁定(屏蔽操作系统的部分功能)
BrowserWindow类创建创建时指定{fullscreen:true,kiosk:true}(配合全屏使用)
页面中动态设置:
const remote=require('electron').remote;
function onClick(){
const win=remote.getCurrentWindow();
If(win.isKiosk()){
win.setKiosk(false);
}else{
win.setKiosk(true);
}
}
设置窗口图标
BrowserWindow类创建创建时指定{icon:’images/tb.ico’}属性。
还可以通过BrowserWindow的setIcon(‘path’)方法
Mac系统下不能设置图标(process.platform==’darwin’) 表示mac
优雅的加载窗口
BrowserWindow类创建时指定{show:false},窗口不会立即显示,当html完全加载后再将窗口显示出来。
win.on('ready-to-show',()=>{
win.show()
})
父子窗口
BrowserWindow类创建创建时指定{parent:parentWin}属性,parentWin是父窗口实例(BrowserWindow)
子窗口永远在父窗口上面,父窗口关闭子窗口也关闭
模态窗口
模态窗口是子窗口,BrowserWindow类创建创建时指定{parent:parentWin,modal:true}属性
窗口之间的传值
使用ipc(interProcess Communication,进程间的通信)方式在窗口之间传递数据。ipcMain和ipcRenderer,ipcMain用于主窗口中;ipcRenderer用于其他窗口
主窗口:window1,其他窗口:window2
window1 -> window2
在window2中通过ipcRenderer的监听事件接收window1传过来的数据
在window2关闭时,会通过ipcRenderer给window1发送一个消息,window1通过ipcMain监听的事件获取 window2发来的消息
对话框
showOpenDialog
OS标准对话框:dialog.showOpenDialog([browserWindow,]options[,calback])
browserWindow允许该对话框附加到父窗口,作为父窗口的模态对话框
callback回调函数,返回选中文件或路径,如果不指定则通过showOpenDialog返回
Options:
title {string} 窗口标题(windows)
defaultPah {string} 默认路径
buttonLable {string} open按钮的文本
filter {array} 过滤文件类型
properties {array} 包含对话框的功能,如打开文件,目录,多选等
message {string} 窗口标题(mac)
打开简单的文件
const remote=require('electron').remote
const dialog=remote.dialog
function onClick_OpenFile(){
const label=documet.getElementById('label')
var options={};
options.title='打开文件';
options.message='打开文件';
options.buttonLabel='选择';
options.defaultPath='/drivers';
options.properties=['openFile','multiSelections'];//文件多选:’multiSelections’
options.filters=[
{name:'图像文件',extensions:['jpg','bmp','png','gif']},
{name:'视频文件',extensions:['mkv','avi','mp4']},
{name:'所有文件(*.*)',extensions:['*']}
];
label.innerText=dialog.showOpenDialog(options)
}
选择目录(创建目录mac,‘createDirectory’ )
const remote=require('electron').remote
const dialog=remote.dialog
function onClick_OpenDir(){
const label=documet.getElementById('label')
var options={};
options.title='选择目录';
options.message='选择目录';
options.buttonLabel='选择';
options.properties=['openDirectory','createDirectory'];//’createDirectory’ mac
label.innerText=dialog.showOpenDialog(options)
}
通过回调函数返回结果
const remote=require('electron').remote
const dialog=remote.dialog
function onClick_OpenDir(){
const label=documet.getElementById('label')
var options={};
options.title='打开文件';
options.message='打开文件';
options.buttonLabel='选择';
options.defaultPath='/drivers';
options.properties=['openFile','multiSelections'];//文件多选:’multiSelections’
options.filters=[
{name:'图像文件',extensions:['jpg','bmp','png','gif']},
{name:'视频文件',extensions:['mkv','avi','mp4']},
{name:'所有文件(*.*)',extensions:['*']}
];
dialog.showOpenDialog(options,(filePaths)=>{
for(var i=0;i<filePaths.length;i++){
label.innerText+=filePaths[i]+'\r\n'
}
})
}
保存对话框
showSaveDialog
OS标准对话框:dialog.showSaveDialog([browserWindow,]options[,calback])
browserWindow允许该对话框附加到父窗口,作为父窗口的模态对话框
callback回调函数,返回选中文件或路径,如果不指定则通过showOpenDialog返回
options:
title {string} 窗口标题(windows)
defaultPah {string} 默认路径
buttonLable {string} open按钮的文本
filter {array} 过滤文件类型
nameFieldLabel:选择文件按钮的名称
const remote=require('electron').remote
const dialog=remote.dialog
function onClick_SaveFile(){
const label=documet.getElementById('label')
var options={};
options.title='保存文件';
options.message='打开文件';
options.buttonLabel='保存';
options.defaultPath='/drivers';
options.nameFieldLabel='输入名称'
options.filters=[
{name:'图像文件',extensions:['jpg','bmp','png','gif']},
{name:'视频文件',extensions:['mkv','avi','mp4']},
{name:'所有文件(*.*)',extensions:['*']}
];
// label.innerText=dialog.showSaveDialog(options)
dialog.showSaveDialog(options,(fileName)=>{
label.innerText=fileName+'\r\n';
})
}
消息框
showMessageBox
OS标准对话框:dialog.showMessageBox([browserWindow,]options[,calback])
browserWindow允许该对话框附加到父窗口,作为父窗口的模态对话框
callback回调函数,返回选中文件或路径,如果不指定则通过showOpenDialog返回
options:
title:标题
message:内容
icon:图标 (使用png图片,一般不考虑尺寸)
type:对话框类型
none,info,error,question,warning
buttons:自定义按钮(数组形式)
const remote=require('electron').remote
const dialog=remote.dialog
function onClick_MessageBox(){
const label=document.getElementByID('label');
var option={
title:'信息',
message:'这是一个消息提示框',
icon:'../../kt.png',
type:'error',
buttons:['按钮1','按钮2','按钮3']
};
//label.innerText=dialog.showMessageBox(options)
dialog.showMessageBox(options,(res)=>{
label.innerText='单击了第'+(res+1)+'个按钮'
});
}
错误对话框
showErrorBox(title,context)
const remote=require('electron').remote;
const dialog=remote.dialog;
function onClick_ErrorBox(){
dialog.showErrorBox('错误','这是一个错误')
}
window.open 创建子窗口
函数原型:window.open(url,[,title],[,attributes])
url:要打开的页面链接(可以是本地,也可以是web)
title:窗口标题,如果页面中已设置title,这个参数会被忽略
attributes:设置窗口的一些属性,例如窗口大小
返回值
BrowserWindowProxy:可以认为是BrowerWindow的代理类
控制窗口
获取窗口焦点 focus
让窗口失去焦点 blur
关闭窗口 close
打印窗口 print
function onClick_OpenWindow(){
//win=window.open('./child.html','子窗口','width=200.height=200')
win=window.open('https://www.baidu.com')
}
function onClick_Focus(){
if(win!=undefined){
win.focus()
}
}
function onClick_Blur(){
if(win!=undefined){
win.blur()
}
}
postMessage方法传递数据
win.postMessage(data,'*')
// 父窗口发送数据
function onClick_SendMessage(){
if(win!=undefined){
//data是元素ID
win.postMessage({'name':data.value},'*')
}
}
//子窗口接收数据
function onLoad(){
windows.addEventListener('message',function(e){
//data是元素ID
data.innerText=e.data.name;
alert(e.origin)//页面来源
});
}
子窗口返回数据
通过ipcMain,ipcRenderer实现
const ipcMain = require('electron').ipcMain;
const ipcRenderer = require('electron').ipcRenderer;
// 子窗口
function winClose(){
const win=remote.getCurrentWindow();
//发送close关闭事件
ipcRenderer.send('close','子窗口关闭');
win.close()
}
// 父窗口
//监听close事件
ipcMain.on('close',(event,str)=>{
alert(str)
});
eval 向子窗口传递方法
eval 方法用来执行javascript代码
//onClick_Eval() 父窗口调用
function onClick_Eval(){
if(win!=undefined){
//eval 中的代码在子窗口中执行
win.eval('data.innerText="'+data.value+'"')
}
}
Webview
<body>
<webview id=’webview1’ src=’./index.html’
Style=’width:600px;height:400px’></webview>
</body>
webview事件
function onload(){
const webview=document.getElementById(‘webview1’);
const loadstart=()=>{
console.log('loadstart');
}
const loadstop=()=>{
console.log('loadstop');
}
//开始加载事件
webview.addEventListener('did-start-loading',loadstart);
//加载完成事件
webview.addEventListener.('did-stop-loading',loadstop);
}
在webview中装载页面中执行node.js API
webview加载的页面默认是不执行nodejs API
添加nodeintegration属性
<webview id='webview1' src='www.baidu.com'
Style='width:600px;height:400px' nodeintegration>
</webview>
webview常用API
function onClick_API(){
webview=document.getElementById('webview1')
//加载指定的页面
webview.loadURL('https://www.baidu.com');
//页面重新加载
webview.reload();
//获取标题
console.log(webview.getTitle())
//获取URL地址
console.log(webview.getURL())
//执行webview页面中的函数 func1
webview.executeJavaScript('func1()'')
}
获取屏幕尺寸和鼠标绝对值
const electron = require('electron');
const remote=electron.remote;
function onClick_Screen(){
const win=remote.getCurrentWindow();
//PC 屏幕大小
const {width,height} = electron.screen.getPrimaryDisplay().workAreaSize;
//设置窗口大小 true表示开启动画效果
win.setSize(width,height,true);
win.setPosition(0,0)
//获取鼠标的绝对位置
console.log('x:'+election.screen.getCursorScreenPoint().x);
console.log('y:'+election.screen.getCursorScreenPoint().y);
}
Windows平台添加进度条
const electron = require('electron');
const remote=electron.remote;
function onClick_ProgressBar(){
const win=remote.getCurrentWindow();
win.setProgressBar(0.3)
}
原生应用菜单
模板创建菜单
const {app,BrowserWindow,Menu}=require('electron');
function createWindow(){
win=new BrowserWindow({width:800,height:600});
win.loadFile('index.html');
//定义菜单模板
const template=[{
label:'文件',
submenu:[ { //子菜单
label:'关于',
role:'about' //角色 弹出模态框 角色自带逻辑动作
click:()=>{
var aboutWin=new BrowserWindow({width:300,height:200, parent:win,modal:true})
}
},{
type:'separator' //分割线
},{
label:'关闭',
accelerator:'Command+Q',//快捷键
click:()=>{
win.close();
}
}
]},
{label:'编辑',submenu:[]}];
if(process.platform=='darwin'){
//添加Mac特有的菜单项
}
const menu=Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
win.on('closed',()=>{
console.log('closed');
win=null;
})
}
菜单类型:
通过type指定 {type:‘checkbox’,label:‘checkBtn’}
1、normal:默认菜单
2、separator:分割线 (type)
3、submenu:子菜单
4、checkbox:多选菜单 (type)
5、radio:单选菜单 (type)
菜单图标:
通过icon属性指定,图标会按原尺寸显示,一般图标大小为16x16
{
label:'文件'
submenu:[ {
label:'打开',
icon:'../../openFolder.png' //图片路径
} ]
}
动态创建图标:
const electron=require('electron');
const remote=electron.remote;
const Menu=remote.Menu;
const MenuItem=remote.MenuItem;
var coustomMenu=new Menu();//动态添加的菜单
function originMenu(){
const menu=new Menu();
var icon=''
if(process.platform=='win32'){
icon='../../folder.ico';
}else{
icon='../../folder.png'
}
var menuItemOpen=new MenuItem({label:'open',icon:icon});
var menuItemSave=new MenuItem({label:'save',click=>{/*... 处理函数 ...*/}});
var menuItemFile=new MenuItem({label:'文件',
submenu:[menuItemOpen,menuItemSave]});
var menuItemCustom=new MenuItem({label:'定制菜单', submenu:coustomMenu})
const menu=new Menu();
menu.append(menuItemFile);
menu.append(menuItemCoustom);
Menu.setApplicationMenu(menu);
}
function AddMenu(){
var type='normal';
if(radio.checked){
type='radio'
}
if(checkbox.checked){
type='checkbox'
}
customMenu.append(new MenuItem({label:menuitem.value,type:type}))//添加菜单
menuitem.value=''
radio.checked=false;
checkbox.checked=false;
Menu.setApplicationMenu(Menu.getApplicationMenu())//让菜单生效
}
上行文菜单
const electron=require('electron');
const remote=electron.remote;
const Menu=remote.Menu;
const MenuItem=remote.MenuItem;
const dialog=remote.dialog;
function winLoad(){
const menu=new Menu();
const win=remote.getCurrentWindow();
var menuItemOpen=new MenuItem({
label:'打开',click:()=>{
var paths=dialog.showOpenDialog({properties:['openFile']});
if(paths!=undefined){
win.setTitle(paths[0])
}
}
})
var menuItemSave=new MenuItem({label:'保存'});
menu.append(menuItemOpen);
menu.append(menuItemSave);
panel.addEventListener('contextmenu',function(event){
event.preventDefault();
menu.popup({x:event.x,y:event.y});
return false;
});
}
应用程序托盘
const { app, Menu, Tray, BrowserWindow } = require('electron')
const path = require('path');
let win;
let tray;
let contextMenu;
function createWindows() {
win = new BrowserWindow({ width: 800, height: 600 });
win.loadFile('index.html');
//创建托盘
var trayIcon = path.join(__dirname, 'images');
tray = new Tray(path.join(trayIcon, 'orderline.png'));
tray.setToolTip('This is my application.');
//系统托盘右键菜单
var trayMenuTemplate = [{
label: '设置',
click: function () { }
}, {
label: '帮助',
click: function () { }
}, {
label: '关于',
click: function () { }
}, {
label: '退出',
click: function () {
app.quit();
}
}
];
contextMenu = Menu.buildFromTemplate(trayMenuTemplate);
tray.setContextMenu(contextMenu);
// win.webContents.openDevTools()
win.on('closed', () => {
win = null
});
}
app.on('ready', createWindows)
app.on('windows-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (win === null) {
createWindows();
}
});
动态设置托盘图标
if(tray!=undefiend){
tray.setImage('../../node.png');
}
动态设置托盘标题(only mac)
if(tray!=undefiend){
tray.setTitle('hello electron');
}
动态设置托盘提示信息
if(tray!=undefiend){
tray.setToolTip('hello electron');
}
动态删除托盘
if(tray!=undefiend){
tray.destroy();
tray=undefined;
}
Windows托盘添加气泡信息
if(tray!=undefiend){
tray.displayBalloon({
title:'有消息了',
icon:'../../msg.png',
content:'消息内容'
});
}
// ----------------------------------------------------
//气泡显示事件
tray.on('ballon-show',()=>{
// ……
})
//气泡单击事件
tray.on('ballon-click',()=>{
// ……
})
//气泡关闭事件
tray.on('ballon-closed',()=>{
// ……
})
拖拽操作
//禁用鼠标经过事件
panel.ondragover=()=>{return false;}
//放下事件
panel.ondrop=(e)=>{
e.preventDefault();//屏蔽默认操作
for(let f of e.dataTransfer.files){
image.src=f.path;
break;
}
}
摄像头操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>摄像头操作</title>
</head>
<body>
<video autoplay style="width: 300px; height:300px"></video>
</body>
<script>
function initialize(){
video=document.querySelector('video')
let errorCallBack=(error)=>{
console.log(`连接视频流失败:${error.message}`);
}
window.navigator.webkitGetUserMedia({video:true},(localStream)=>{
video.src=window.URL.createObjectURL(localStream)
},errorCallBack)
}
window.onload=initialize();
</script>
</html>
Electron-packager使用
安装:npm install electron-packager -g
基本打包命令:
进入到工程目录
# 方式1
electron-packager . --platform=win32 --arch=x64 --icon trainmpa.ico --out=./out --asar --electron-version=4.2.12 --executable-name trainMap
# 方式2
electron-packager . --electron-version=4.2.12 --executable-name appName
资源文件处理:
1、复制资源文件到打包后的目录中
2、开发时资源放到工程目录下,直接打包
3、尽可能使用web资源
修改应用程序名称:
1、修改工程中的package.json,再打包
2、打包时指定程序名称:
electron-packager . appName --electron-version=4.2.12
修改应用程序图标:
添加icon属性:
--icon=’图标路径’
Windows图标是ico
Mac图标icns
生成asar文件(打包工程源码)
添加asar参数
electron-packager . appName --asar --electron-version=4.2.12
提取asar
asar extract app.asar xyz(将app.asar文件内容提取到xyz文件夹内)
元信息潜入到exe(windows)
使用win32metadata参数
--win32metadata.CompanyName:公司名称
--win32metadata.ProductName:产品名称
--win32metadata.FileDescription:文件描述
--win32metadata.OriginalFilename:原始文件名
electron-packager-interactive 工具
根据提示一步一步打包工具