无星的electron之旅(十二)—— 如何像浏览器一样制作右键菜单

一、背景

接了个小需求,已有网站,做了个electron的壳,需要添加右键菜单
1.复制图片为base64
2.另存为图片
3.复制图片地址

二、首先需要添加右键菜单

1.主线程 background.js添加右键菜单内容

// background.js
///////////
const rightMenu = initRightMenu();
function initRightMenu() {
  const rightTemplate = [
    {
      label: "复制图片为base64",
      click: () => {
        console.log("复制图片为base64");
      },
    },
    {
      label: "另存为图片",
      click: () => {
        console.log("另存为图片");
      },
    },
    {
      label: "复制图片URL",
      click: () => {
        console.log("复制图片URL");
      },
    },
  ];
  const menu = Menu.buildFromTemplate(rightTemplate);
  return menu;
}
///////////

2.页面右键要能调用

需要页面右键能够响应

那么我们需要在页面上能够获取到右键的点击事件

// 页面入口,比如vue的main.js
window.addEventListener("contextmenu", (e) => {
  e.preventDefault();
  // TODO 通知主线程
});

3.通知主线程响应

通过preload为window对象挂上一个rightMenu属性用于沟通调用

// preload.js
contextBridge.exposeInMainWorld("rightMenu", {
  show: (domId) =>
    ipcRenderer.invoke("show-context-menu", {
      domId,
    }),
});

// main.js
window.addEventListener("contextmenu", (e) => {
  e.preventDefault();
  const domName = e.target.localName;
  if (domName === "img") {
    if (e.target.id === "") {
      const date = new Date().valueOf();
      e.target.id = date;
    }
    window.rightMenu.show(e.target.id);
  }
});

因为我们的目标是图片,所以判断一下,是图片才调用右键展示

同时我们需要知道我们需要操作哪个dom,所以为其设置一个id,方便后续操作

// background.js
ipcMain.handle("show-context-menu", (event, args) => {
  const { domId } = args;
  clickDomId = domId;
  rightMenu.popup({
    window: BrowserWindow.getFocusedWindow().webContents,
  });
});

展示

4.实现内容

好像有点简单,随便百度了几个方法,就不多说了,直接放代码

// preload.js
ipcRenderer.on("base64", (event, args) => {
  const { domId } = args;
  const element = document.getElementById(domId);
  imageToBase64(element.currentSrc);
});

ipcRenderer.on("saveImage", (event, args) => {
  const { domId } = args;
  const element = document.getElementById(domId);
  const nameArray = element.currentSrc.split("/");
  const tmpArray = nameArray[nameArray.length - 1].split(".");
  const name = tmpArray[0];
  // console.log(nameArray);
  download(element.currentSrc, name);
});

ipcRenderer.on("copyUrl", (event, args) => {
  const { domId } = args;
  const element = document.getElementById(domId);
  copy(element.currentSrc);
});

// base64
const getBase64Image = (img) => {
  const canvas = document.createElement("canvas");
  canvas.width = img.width;
  canvas.height = img.height;
  const ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0, img.width, img.height);
  let ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
  if (ext === "jpg") {
    ext = "jpeg"; //这个地方是由于如果是jpg, 他会识别成image/png
  }
  const dataURL = canvas.toDataURL("image/" + ext);
  return dataURL;
};

const imageToBase64 = (url) => {
  let image = new Image();
  image.crossOrigin = "";
  image.src = url;
  image.onload = function () {
    const base64 = getBase64Image(image);
    // console.log(base64);
    copy(base64);
  };
};

const download = (data, name) => {
  if (!data) {
    return;
  }
  getUrlBase64(data).then((base64) => {
    let a = document.createElement("a");
    a.style.display = "none";
    a.download = name;
    a.href = base64;
    document.body.appendChild(a);
    a.click();
  });
};
const getUrlBase64 = (url) => {
  return new Promise((resolve) => {
    let canvas = document.createElement("canvas");
    let ctx = canvas.getContext("2d");
    let img = new Image();
    img.crossOrigin = "Anonymous"; //允许跨域
    img.src = url;
    img.onload = function () {
      canvas.height = 300;
      canvas.width = 300;
      ctx.drawImage(img, 0, 0, 300, 300);
      let dataURL = canvas.toDataURL("image/png");
      canvas = null;
      resolve(dataURL);
    };
  });
};

那么如果是加载别人远程页面,没有页面代码,比如我们是个浏览器,如何实现这个功能?

其实也有方法,我们可以通过注入的方式去做

可见无星的electron之旅(九)—— JS注入

在页面加载完成以后,通过preload注入代码即可

// preload.js
document.addEventListener("DOMContentLoaded", () => {
  // 页面内容加载之后需要引入的一些操作
  window.addEventListener("contextmenu", (e) => {
    e.preventDefault();
    const domName = e.target.localName;
    if (domName === "img") {
      if (e.target.id === "") {
        const date = new Date().valueOf();
        e.target.id = date;
      }
      // window.rightMenu.show(e.target.id);
      ipcRenderer.invoke("show-context-menu", {
        domId: e.target.id,
      });
    }
  });
});

通过这段代码,我们可以为不同样式标签添加不同操作,并且不需要修改网页代码,自然像浏览器一样,添加右键菜单

效果

1.gif
1.gif
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容