L
O
A
D
I
N
G

右键菜单


增加右键菜单

同主题的小伙伴可能发现了,博客网站的右键菜单实在是功能少的可怜,一个好的右键菜单可以让访客朋友的用户体验提高不少,所以下面我们就来就是实现给我买的博客网站添加一个功能更丰富的右键菜单😋

效果

右键菜单

实现

第一步,定位到主题文件夹下的页面布局代码,即matery/layout/layout.ejs,在<main>标签中加入:

<!-- 添加右键菜单 -->
<style>
    #rightMenu {
        display: none;
        position: fixed;
        padding: 0 .25rem;
        width: 10rem;
        height: fit-content;
        top: 10%;
        left: 10%;
        background-color: rgba(255, 255, 255, .85);
        -webkit-backdrop-filter: blur(20px);
        backdrop-filter: blur(20px);
        border-radius: 12px;
        z-index: 99994;
        border: 1px solid #e3e8f7;
        user-select: none;
        box-shadow: 0 0 12px 4px rgba(0, 0, 0, .05);
    }

    #rightMenu:hover {
        border: 1px solid #177cb0;
        box-shadow: 0 8px 12px -3px #4259ef23;
    }

    #rightMenu .rightMenu-group {
        padding: .35rem .3rem;
        transition: .3s;
    }

    #rightMenu .rightMenu-line {
        border-top: 1px dashed #4259ef23;
    }

    #rightMenu .rightMenu-group.rightMenu-small {
        display: flex;
        justify-content: space-between;
    }

    #rightMenu .rightMenu-group .rightMenu-item {
        border-radius: 8px;
        transition: .3s;
        cursor: pointer;
        color: #525f7f;
    }

    #rightMenu .rightMenu-line .rightMenu-item {
        margin: .25rem 0;
        padding: .25rem 0;
    }

    #rightMenu .rightMenu-group.rightMenu-line .rightMenu-item {
        display: flex;
    }

    #rightMenu .rightMenu-group .rightMenu-item:hover {
        background-color: #177cb0;
        color: #fff;
        box-shadow: 0 8px 12px -3px #4259ef23;
    }

    #rightMenu .rightMenu-group .rightMenu-item:active {
        transform: scale(.97);
    }

    #rightMenu .rightMenu-group .rightMenu-item i {
        display: inline-block;
        text-align: center;
        line-height: 1.5rem;
        width: 2rem;
        padding: 0 .25rem;
    }

    #rightMenu .rightMenu-line .rightMenu-item i {
        margin: 0 .25rem;
    }

    #rightMenu .rightMenu-group .rightMenu-item span {
        line-height: 1.5rem;
    }

    .rightMenu-small .rightMenu-item {
        width: 30px;
        height: 30px;
        line-height: 30px;
    }

    #rightmenu-mask {
        position: fixed;
        width: 100vw;
        height: 100vh;
        background: 0 0;
        top: 0;
        left: 0;
        display: none;
        z-index: 101;
        margin: 0 !important;
        z-index: 99993;
    }
</style>

<div id="rightMenu" style="top: 0px; left: 0px; display: none;">
    <div class="rightMenu-group rightMenu-small">
        <div class="rightMenu-item" id="menu-backward" title="返回"><i class="fas fa-arrow-left"></i></div>
        <div class="rightMenu-item" id="menu-forward" title="前进"><i class="fas fa-arrow-right"></i></div>
        <div class="rightMenu-item" id="menu-refresh" title="刷新"><i class="fas fa-arrow-rotate-right"></i></div>
        <div class="rightMenu-item" id="menu-top" title="返回顶部"><i class="fas fa-arrow-up"></i></div>
    </div>
    <div class="rightMenu-group rightMenu-line rightMenuPlugin" style="display:none">
        <div class="rightMenu-item" id="menu-copytext" style="display:none"><i class="fas fa-copy"></i><span>复制选中文本</span></div>
        <div class="rightMenu-item" id="menu-copylink" style="display:none"><i class="fas fa-link"></i><span>复制链接地址</span></div>
        <div class="rightMenu-item" id="menu-pastetext" style="display:none"><i class="fas fa-paste"></i><span>粘贴文本</span></div>
        <div class="rightMenu-item" id="menu-commenttext" style="display:none"><i class="fas fa-comment-medical"></i><span>引用到评论</span></div>
        <div class="rightMenu-item" id="menu-newwindow" style="display:none"><i class="fas fa-window-restore"></i><span>新窗口打开</span></div>
        <div class="rightMenu-item" id="menu-copyimg" style="display:none"><i class="fas fa-images"></i><span>复制此图片</span></div>
        <div class="rightMenu-item" id="menu-downloadimg" style="display:none"><i class="fas fa-download"></i><span>下载此图片</span></div>
        <div class="rightMenu-item" id="menu-search" style="display:none"><i class="fas fa-magnifying-glass"></i><span>站内搜索</span></div>
        <div class="rightMenu-item" id="menu-searchBing" style="display:none"><i class="fas fa-magnifying-glass"></i><span>必应搜索</span></div>
        <div class="rightMenu-item" id="menu-music-toggle" style="display:none"><i class="fas fa-play"></i><span>播放音乐</span></div>
        <div class="rightMenu-item" id="menu-music-back" style="display:none"><i class="fas fa-backward"></i><span>切换到上一首</span></div>
        <div class="rightMenu-item" id="menu-music-forward" style="display:none"><i class="fas fa-forward"></i><span>切换到下一首</span></div>
        <div class="rightMenu-item" id="menu-music-playlist" onclick="window.open(&quot;https://music.163.com/#/playlist?app_version=8.8.36&amp;id=5197802668&quot;,&quot;_blank&quot;)" style="display:none"><i class="fas fa-radio"></i><span>查看所有歌曲</span></div>
        <div class="rightMenu-item" id="menu-music-copyMusicName" style="display:none"><i class="fas fa-copy"></i><span>复制歌名</span></div>
    </div>
    <div class="rightMenu-group rightMenu-line rightMenuOther">
        <a class="rightMenu-item menu-link" id="menu-randomWebsite" target="_blank" rel="noopener" href="https://travel.moe/go.html"><i class="fas fa-paper-plane"></i><span>次元跃迁</span></a>
        <a class="rightMenu-item menu-link" id="menu-randomPost"><i class="fas fa-shoe-prints"></i><span>随便逛逛</span></a>
        <a class="rightMenu-item menu-link" href="/categories/"><i class="fas fa-cube"></i><span>博客分类</span></a>
        <a class="rightMenu-item menu-link" href="/tags/"><i class="fas fa-tags"></i><span>文章标签</span></a>
    </div>
    <div class="rightMenu-group rightMenu-line">
        <a class="rightMenu-item menu-link" href="/privacy/"><i class="fas fa-hand"></i><span>隐私协议</span></a>
        <a class="rightMenu-item menu-link" href="/cc/"><i class="fas fa-closed-captioning"></i><span>版权协议</span></a>
        <a class="rightMenu-item menu-link" href="/SEAEPOCH-WRITING-STANDARDS/"><i class="fas fa-feather"></i><span>行文规范</span></a>
    </div>
    <div class="rightMenu-group rightMenu-line rightMenuOther">
        <div class="rightMenu-item" id="menu-copy"><i class="fas fa-copy"></i><span>复制地址</span></div>
        <div class="rightMenu-item" id="menu-darkmode"><i class="fas fa-moon"></i><span class="menu-darkmode-text">深色模式</span></div>
    </div>
</div>
<div id="rightmenu-mask" style="display: none"></div>

<script src="/js/rightMenu.js"></script>
<script>addRightMenuClickEvent()</script>

matery/source/js下新建rightMenu.js,添加

// 初始化函数
let rm = {};
var btf = {
  scrollToDest: (pos, time) => {
    if (pos < 0 || time < 0) {
      return
    }

    const currentPos = window.scrollY || window.screenTop
    pos = pos - 70

    if ('CSS' in window && CSS.supports('scroll-behavior', 'smooth')) {
      window.scrollTo({
        top: pos,
        behavior: 'smooth'
      })
      return
    }

    let start = null
    time = time || 500
    window.requestAnimationFrame(function step(currentTime) {
      start = !start ? currentTime : start
      if (currentPos < pos) {
        const progress = currentTime - start
        window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos)
        if (progress < time) {
          window.requestAnimationFrame(step)
        } else {
          window.scrollTo(0, pos)
        }
      } else {
        const progress = currentTime - start
        window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time))
        if (progress < time) {
          window.requestAnimationFrame(step)
        } else {
          window.scrollTo(0, pos)
        }
      }
    })
  }
}
var heo = {

  // 下载图片
  downloadImage: function (imgsrc, name) { //下载图片地址和图片名
    rm.hideRightMenu();
    if (rm.downloadimging == false) {
      rm.downloadimging = true;
      M.toast({
        html: '<span>正在下载中,请稍后</span>'
      }, 10000);
      setTimeout(function () {
        let image = new Image();
        // 解决跨域 Canvas 污染问题
        image.setAttribute("crossOrigin", "anonymous");
        image.onload = function () {
          let canvas = document.createElement("canvas");
          canvas.width = image.width;
          canvas.height = image.height;
          let context = canvas.getContext("2d");
          context.drawImage(image, 0, 0, image.width, image.height);
          let url = canvas.toDataURL("image/png"); //得到图片的base64编码数据
          let a = document.createElement("a"); // 生成一个a元素
          let event = new MouseEvent("click"); // 创建一个单击事件
          a.download = name || "photo"; // 设置图片名称
          a.href = url; // 将生成的URL设置为a.href属性
          a.dispatchEvent(event); // 触发a的单击事件
        };
        image.src = imgsrc;
        M.toast({
          html: '<span>图片已添加盲水印,请遵守版权协议</span>'
        }, 5000);
        rm.downloadimging = false;
      }, "10000");
    } else {
      M.toast({
        html: '<span>有正在进行中的下载,请稍后再试</span>'
      }, 5000);
    }
  },

  //切换音乐播放状态
  musicToggle: function () {
    console.log("等待开发...");
  },

  //音乐上一曲
  musicSkipBack: function () {
    console.log("等待开发...");
  },

  //音乐下一曲
  musicSkipForward: function () {
    console.log("等待开发...");
  },

  //获取音乐中的名称
  musicGetName: function () {
    console.log("等待开发...");
  }
}

//禁止图片拖拽
rm.stopdragimg = $("img");
rm.stopdragimg.on("dragstart", function () {
  return false;
});

// 显示菜单
rm.showRightMenu = function (isTrue, x = 0, y = 0) {
  let $rightMenu = $('#rightMenu');
  $rightMenu.css('top', x + 'px').css('left', y + 'px');
  if (isTrue) {
    $rightMenu.show();
    stopMaskScroll()
  } else {
    $rightMenu.hide();
  }
}

// 隐藏菜单
rm.hideRightMenu = function () {
  rm.showRightMenu(false);
  $('#rightmenu-mask').attr('style', 'display: none');
}

// 尺寸
let rmWidth = $('#rightMenu').width();
let rmHeight = $('#rightMenu').height();

// 重新定义尺寸
rm.reloadrmSize = function () {
  rmWidth = $('#rightMenu').width();
  rmHeight = $('#rightMenu').height();
}

// 获取点击的href
let domhref = '';
let domImgSrc = '';
let globalEvent = null;

// 监听右键初始化
window.oncontextmenu = function (event) {
  if (document.body.clientWidth > 768) {
    let pageX = event.clientX + 10; //加10是为了防止显示时鼠标遮在菜单上
    let pageY = event.clientY;
    // console.log(event);

    //其他额外菜单
    let $rightMenuOther = $('.rightMenuOther');
    let $rightMenuPlugin = $('.rightMenuPlugin');
    let $rightMenuCopyText = $('#menu-copytext');
    let $rightMenuPasteText = $('#menu-pastetext');
    let $rightMenuCommentText = $('#menu-commenttext');
    let $rightMenuNewWindow = $('#menu-newwindow');
    let $rightMenuCopyLink = $('#menu-copylink');
    let $rightMenuCopyImg = $('#menu-copyimg');
    let $rightMenuDownloadImg = $('#menu-downloadimg');
    let $rightMenuSearch = $('#menu-search');
    let $rightMenuSearchBing = $('#menu-searchBing');
    let $rightMenuMusicToggle = $('#menu-music-toggle');
    let $rightMenuMusicBack = $('#menu-music-back');
    let $rightMenuMusicForward = $('#menu-music-forward');
    let $rightMenuMusicPlaylist = $('#menu-music-playlist');
    let $rightMenuMusicCopyMusicName = $('#menu-music-copyMusicName');
    let href = event.target.href;
    let imgsrc = event.target.currentSrc;

    // 判断模式 扩展模式为有事件
    let pluginMode = false;
    $rightMenuOther.show();
    globalEvent = event;

    // 检查是否需要复制 是否有选中文本
    if (selectTextNow && window.getSelection()) {
      pluginMode = true;
      $rightMenuCopyText.show();
      $rightMenuCommentText.show();
      $rightMenuSearch.show();
      $rightMenuSearchBing.show();
    } else {
      $rightMenuCopyText.hide();
      $rightMenuCommentText.hide();
      $rightMenuSearchBing.hide();
      $rightMenuSearch.hide();
    }

    //检查是否右键点击了链接a标签
    if (href) {
      pluginMode = true;
      $rightMenuNewWindow.show();
      $rightMenuCopyLink.show();
      domhref = href;
    } else {
      $rightMenuNewWindow.hide();
      $rightMenuCopyLink.hide();
    }

    //检查是否需要复制图片
    if (imgsrc) {
      pluginMode = true;
      $rightMenuCopyImg.show();
      $rightMenuDownloadImg.show();
      domImgSrc = imgsrc;
    } else {
      $rightMenuCopyImg.hide();
      $rightMenuDownloadImg.hide();
    }

    // 判断是否为输入框
    if (event.target.tagName.toLowerCase() === 'input' || event.target.tagName.toLowerCase() === 'textarea') {
      console.log('这是一个输入框')
      pluginMode = true;
      $rightMenuPasteText.show();
    } else {
      $rightMenuPasteText.hide();
    }

    //判断是否是音乐
    if (event.target.nodeName == "METING-JS") {
      console.log('这是一个音乐');
      pluginMode = true;
      $rightMenuMusicToggle.show();
      $rightMenuMusicBack.show();
      $rightMenuMusicForward.show();
      $rightMenuMusicPlaylist.show();
      $rightMenuMusicCopyMusicName.show();
    } else {
      $rightMenuMusicToggle.hide();
      $rightMenuMusicBack.hide();
      $rightMenuMusicForward.hide();
      $rightMenuMusicPlaylist.hide();
      $rightMenuMusicCopyMusicName.hide()
    }

    // 如果不是扩展模式则隐藏扩展模块
    if (pluginMode) {
      $rightMenuOther.hide();
      $rightMenuPlugin.show();
    } else {
      $rightMenuPlugin.hide()
    }

    rm.reloadrmSize()

    // 鼠标默认显示在鼠标右下方,当鼠标靠右或考下时,将菜单显示在鼠标左方\上方
    if (pageX + rmWidth > window.innerWidth) {
      pageX -= rmWidth + 10;
    }
    if (pageY + rmHeight > window.innerHeight) {
      pageY -= pageY + rmHeight - window.innerHeight;
    }

    rm.showRightMenu(true, pageY, pageX);
    $('#rightmenu-mask').attr('style', 'display: flex');
    return false;
  }
};

// 下载图片状态
rm.downloadimging = false;

// 复制图片到剪贴板
rm.writeClipImg = function (imgsrc) {
  // console.log('按下复制');
  rm.hideRightMenu();
  M.toast({
    html: '<span>正在下载中,请稍后</span>'
  }, 10000);
  if (rm.downloadimging == false) {
    rm.downloadimging = true;
    setTimeout(function () {
      copyImage(imgsrc);
      M.toast({
        html: '<span>复制成功!图片已添加盲水印,请遵守版权协议</span>'
      }, 5000);
      rm.downloadimging = false;
    }, "10000")
  }
}

function imageToBlob(imageURL) {
  const img = new Image;
  const c = document.createElement("canvas");
  const ctx = c.getContext("2d");
  img.crossOrigin = "";
  img.src = imageURL;
  return new Promise(resolve => {
    img.onload = function () {
      c.width = this.naturalWidth;
      c.height = this.naturalHeight;
      ctx.drawImage(this, 0, 0);
      c.toBlob((blob) => {
        // here the image is a blob
        resolve(blob)
      }, "image/png", 0.75);
    };
  })
}

async function copyImage(imageURL) {
  const blob = await imageToBlob(imageURL)
  const item = new ClipboardItem({
    "image/png": blob
  });
  navigator.clipboard.write([item]);
}

rm.switchDarkMode = function () {
  switchNightMode();
  rm.hideRightMenu();
}

rm.copyUrl = function (id) {
  $("body").after("<input id='copyVal'></input>");
  var text = id;
  var input = document.getElementById("copyVal");
  input.value = text;
  input.select();
  input.setSelectionRange(0, input.value.length);
  document.execCommand("copy");
  $("#copyVal").remove();
}

function stopMaskScroll() {
  if (document.getElementById("rightmenu-mask")) {
    let xscroll = document.getElementById("rightmenu-mask");
    xscroll.addEventListener("mousewheel", function (e) {
      //阻止浏览器默认方法
      rm.hideRightMenu();
      // e.preventDefault();
    }, false);
  }
  if (document.getElementById("rightMenu")) {
    let xscroll = document.getElementById("rightMenu");
    xscroll.addEventListener("mousewheel", function (e) {
      //阻止浏览器默认方法
      rm.hideRightMenu();
      // e.preventDefault();
    }, false);
  }
}

rm.rightmenuCopyText = function (txt) {
  if (navigator.clipboard) {
    navigator.clipboard.writeText(txt);
  }
  rm.hideRightMenu();
}

rm.copyPageUrl = function () {
  var selURL = window.location.href;
  rm.copyUrl(selURL);
  M.toast({
    html: '<span>复制本页链接地址成功</span>'
  }, 2000);
  rm.hideRightMenu();
}

rm.sharePage = function () {
  var content = window.location.href;
  rm.copyUrl(selURL);
  M.toast({
    html: '<span>复制本页链接地址成功</span>'
  }, 2000);
  rm.hideRightMenu();
}

// 复制当前选中文本
var selectTextNow = '';
document.onmouseup = document.ondbclick = selceText;

function selceText() {
  var txt;
  if (document.selection) {
    txt = document.selection.createRange().text;
  } else {
    txt = window.getSelection() + '';
  }
  if (txt) {
    selectTextNow = txt;
    // console.log(selectTextNow);
  } else {
    selectTextNow = '';
  }
}

// 读取剪切板
rm.readClipboard = function () {
  if (navigator.clipboard) {
    navigator.clipboard.readText().then(clipText => rm.insertAtCaret(globalEvent.target, clipText));
  }
}

// 粘贴文本到焦点
rm.insertAtCaret = function (elemt, value) {
  const startPos = elemt.selectionStart,
    endPos = elemt.selectionEnd;
  if (document.selection) {
    elemt.focus();
    var sel = document.selection.createRange();
    sel.text = value;
    elemt.focus();
  } else {
    if (startPos || startPos == '0') {
      var scrollTop = elemt.scrollTop;
      elemt.value = elemt.value.substring(0, startPos) + value + elemt.value.substring(endPos, elemt.value.length);
      elemt.focus();
      elemt.selectionStart = startPos + value.length;
      elemt.selectionEnd = startPos + value.length;
      elemt.scrollTop = scrollTop;
    } else {
      elemt.value += value;
      elemt.focus();
    }
  }
}

//粘贴文本
rm.pasteText = function () {
  const result = rm.readClipboard() || '';
  rm.hideRightMenu();
}

//引用到评论
rm.rightMenuCommentText = function (txt) {
  rm.hideRightMenu();
  var input = document.getElementById('veditor');
  let evt = document.createEvent('HTMLEvents');
  evt.initEvent('input', true, true);
  let inputValue = replaceAll(txt, '\n', '\n> ')
  input.value = '> ' + inputValue + '\n\n';
  input.dispatchEvent(evt);
  var domTop = document.querySelector("#vcomments").offsetTop;
  window.scrollTo(0, domTop - 80);
  input.focus();
  input.setSelectionRange(-1, -1);
}

//替换所有内容
function replaceAll(string, search, replace) {
  return string.split(search).join(replace);
}

// Bing搜索
rm.searchBing = function () {
  M.toast({
    html: '<span>即将跳转到必应搜索</span>'
  }, 2000);
  setTimeout(function () {
    window.open('https://cn.bing.com/search?q=' + selectTextNow);
  }, "1000");
  rm.hideRightMenu();
}

//分享链接
rm.copyLink = function () {
  rm.rightmenuCopyText(domhref);
  M.toast({
    html: '<span>已复制链接地址</span>'
  }, 5000);
}

function addRightMenuClickEvent() {
  // 添加点击事件
  $('#menu-backward').on('click', function () {
    window.history.back();
    rm.hideRightMenu();
  });
  $('#menu-forward').on('click', function () {
    window.history.forward();
    rm.hideRightMenu();
  });
  $('#menu-refresh').on('click', function () {
    window.location.reload();
  });
  $('#menu-top').on('click', function () {
    btf.scrollToDest(0, 500);
    rm.hideRightMenu();
  });
  $('.menu-link').on('click', rm.hideRightMenu);
  $('#menu-darkmode').on('click', rm.switchDarkMode);
  $('#menu-home').on('click', function () {
    window.location.href = window.location.origin;
  });
  $('#menu-randomPost').on('click', function () {
    toRandomPost()
  });
  $('#rightmenu-mask').on('click', rm.hideRightMenu);
  $('#rightmenu-mask').contextmenu(function () {
    rm.hideRightMenu();
    return false;
  });
  $('#menu-copy').on('click', rm.copyPageUrl);
  $('#menu-pastetext').on('click', rm.pasteText);
  $('#menu-copytext').on('click', function () {
    rm.rightmenuCopyText(selectTextNow);
    M.toast({
      html: '<span>复制成功,复制或转载请标注文本位置</span>'
    }, 5000);
  });
  $('#menu-commenttext').on('click', function () {
    rm.rightMenuCommentText(selectTextNow);
  });
  $('#menu-newwindow').on('click', function () {
    window.open(domhref);
    rm.hideRightMenu();
  });
  $('#menu-copylink').on('click', rm.copyLink);
  $('#menu-downloadimg').on('click', function () {
    heo.downloadImage(domImgSrc, 'SeaYJImageDownload');
  });
  $('#menu-copyimg').on('click', function () {
    rm.writeClipImg(domImgSrc);
  });
  $('#menu-search').on('click', function () {
    rm.hideRightMenu();
    $('#searchIcon').click();
    let t = document.getElementById('searchInput');
    let evt = document.createEvent('HTMLEvents');
    evt.initEvent('input', true, true);
    t.value = selectTextNow;
    t.dispatchEvent(evt);
  });
  $('#menu-searchBing').on('click', rm.searchBing);
  //音乐
  $('#menu-music-toggle').on('click', heo.musicToggle);
  $('#menu-music-back').on('click', heo.musicSkipBack);
  $('#menu-music-forward').on('click', heo.musicSkipForward);
  $('#menu-music-copyMusicName').on('click', function () {
    rm.rightmenuCopyText(heo.musicGetName());
    M.toast({
      html: '<span>复制歌曲名称成功</span>'
    }, 3000);
  });
}

现在你就可以本地部署并查看效果了

参考文章

【Hexo博客】魔改美化 Butterfly 主题右键菜单 | 百里飞洋 (meta-code.top)

SeaYJ’s Blog


文章作者: loyeh
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 loyeh !
评论
  目录