diff --git a/README.md b/README.md index cdc411b..218385f 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,12 @@ ![translate.png](translate/translate.png) +![translate.png](translate/translate-android.png) + 特色: * 划词翻译调用“金山词霸、有道词典(有道翻译)、Google Translate(谷歌翻译)、沪江小D、搜狗翻译、必应词典(必应翻译)、Microsoft Translator(必应在线翻译)、海词词典、百度翻译、Oxford Learner's Dictionaries、Oxford Dictionaries、Merriam-Webster、PDF 划词翻译、Google Search、Bing Search(必应搜索)、百度搜索、Wikipedia Search(维基百科搜索)”网页翻译 +* 支持浏览器:Google Chrome、Firefox、Safari、Firefox for Android(无拖动) * 支持顺序自定义 -* 支持隐藏图标 * 支持显示更多图标 * 支持图标拖动 @@ -17,10 +19,12 @@ ![translate-dictionary.png](translate/translate-dictionary.png) +![translate-dictionary.png](translate/translate-dictionary-android.png) + 特色: * 划词翻译调用“有道词典(有道翻译)、金山词霸、Bing 词典(必应词典)、剑桥高阶、沪江小D、谷歌翻译” +* 支持浏览器:Google Chrome、Firefox、Safari(无发音)、Firefox for Android(无拖动) * 支持发音 -* 支持显示当前翻译引擎 * 支持面板拖动 * 支持面板自动调整位置 diff --git a/translate/translate-android.png b/translate/translate-android.png new file mode 100644 index 0000000..af3b7e7 Binary files /dev/null and b/translate/translate-android.png differ diff --git a/translate/translate-dictionary-android.png b/translate/translate-dictionary-android.png new file mode 100644 index 0000000..23fea6f Binary files /dev/null and b/translate/translate-dictionary-android.png differ diff --git a/translate/translate-dictionary.js b/translate/translate-dictionary.js index e30be9e..779c408 100644 --- a/translate/translate-dictionary.js +++ b/translate/translate-dictionary.js @@ -1,7 +1,7 @@ // ==UserScript== // @name 划词翻译:多词典查询 // @namespace http://tampermonkey.net/ -// @version 6.2 +// @version 6.3 // @description 划词翻译调用“有道词典(有道翻译)、金山词霸、Bing 词典(必应词典)、剑桥高阶、沪江小D、谷歌翻译” // @author https://github.com/barrer // @match http://*/* @@ -24,13 +24,14 @@ // Your code here... /**样式*/ var style = document.createElement('style'); - // <--- 可以自定义的变量 + // >>>>> 可以自定义的变量 var fontSize = 14; // 字体大小 var iconWidth = 300; // 整个面板宽度 var iconHeight = 400; // 整个面板高度 - // 可以自定义的变量 ---> (自定义变量修改后把 “@version” 版本号改为 “10000” 防止更新后消失) + // 可以自定义的变量 <<<<< (自定义变量修改后把 “@version” 版本号改为 “10000” 防止更新后消失) var trContentWidth = iconWidth - 16; // 整个面板宽度 - 边距间隔 = 翻译正文宽度 - var trContentHeight = iconHeight - 31; // 整个面板高度 - 边距间隔 = 翻译正文高度 + var trContentHeight = iconHeight - 35; // 整个面板高度 - 边距间隔 = 翻译正文高度 + var zIndex = '2147483647'; // 渲染图层 style.textContent = ` /*组件样式*/ :host{all:unset!important} @@ -39,18 +40,18 @@ a{color:#00c;text-decoration:none;cursor:pointer} a:hover{text-decoration:none} a:active{text-decoration:underline} - img{cursor:pointer;display:inline-block;width:16px;height:16px;border:1px solid #dfe1e5;border-radius:4px;background-color:rgba(255,255,255,1);padding:2px;margin:0;margin-right:5px;box-sizing:content-box;vertical-align:middle} + img{cursor:pointer;display:inline-block;width:20px;height:20px;border:1px solid #dfe1e5;border-radius:4px;background-color:rgba(255,255,255,1);padding:2px;margin:0;margin-right:5px;box-sizing:content-box;vertical-align:middle} img:last-of-type{margin-right:auto} img:hover{border:1px solid #f90} img[activate]{border:1px solid #f90} img[activate]:hover{border:1px solid #f90} table{font-size:inherit;color:inherit} - tr-icon{display:none;position:absolute;padding:0;margin:0;cursor:move;box-sizing:content-box;font-size:${fontSize}px;text-align:left;border:0;border-radius:4px;color:black;z-index:2147483647;background:transparent} + tr-icon{display:none;position:absolute;padding:0;margin:0;cursor:move;box-sizing:content-box;font-size:${fontSize}px;text-align:left;border:0;border-radius:4px;color:black;z-index:${zIndex};background:transparent} tr-icon[activate]{background:#fff;-webkit-box-shadow:0 3px 8px 0 rgba(0,0,0,0.2),0 0 0 0 rgba(0,0,0,0.08);box-shadow:0 3px 8px 0 rgba(0,0,0,0.2),0 0 0 0 rgba(0,0,0,0.08)} tr-audio{display:block;margin-bottom:5px} tr-audio a{margin-right:1em;font-size:80%} tr-audio a:last-of-type{margin-right:auto} - tr-content{display:block;width:${trContentWidth}px;height:${trContentHeight}px;overflow-x:hidden;overflow-y:scroll;background:white;padding:2px 8px;margin-top:5px;box-sizing:content-box;font-family:"Helvetica Neue","Helvetica","Arial","sans-serif";font-size:${fontSize}px;font-weight:normal;line-height:normal;-webkit-font-smoothing:auto;font-smoothing:auto;text-rendering:auto} + tr-content{display:none;width:${trContentWidth}px;height:${trContentHeight}px;overflow-x:hidden;overflow-y:scroll;background:white;padding:2px 8px;margin-top:5px;box-sizing:content-box;font-family:"Helvetica Neue","Helvetica","Arial","sans-serif";font-size:${fontSize}px;font-weight:normal;line-height:normal;-webkit-font-smoothing:auto;font-smoothing:auto;text-rendering:auto} tr-engine~tr-engine{margin-top:1em} tr-engine .title{color:#00c;display:inline-block;font-weight:bold} tr-engine .title:hover{text-decoration:none} @@ -131,7 +132,9 @@ selected, // 当前选中文本 engineId, // 当前翻译引擎 engineTriggerTime, // 引擎触发时间(milliseconds) - idsType; // 当前翻译面板内容列表数组 + idsType, // 当前翻译面板内容列表数组 + pageX, // 图标显示的 X 坐标 + pageY; // 图标显示的 Y 坐标 // 初始化内容面板 content.appendChild(contentList); // 发音缓存 @@ -257,7 +260,8 @@ showContent(); }, { headers: { - 'Cookie': 'HJ_SID=' + uuid() + '; HJ_SSID_3=' + uuid() + '; HJ_CST=1; HJ_CSST_3=1; HJ_UID=' + uuid() + 'Cookie': 'HJ_SID=' + uuid() + '; HJ_SSID_3=' + uuid() + '; HJ_CST=1; HJ_CSST_3=1; HJ_UID=' + uuid(), + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36' } }); }; @@ -376,37 +380,10 @@ } }); // 鼠标事件:防止选中的文本消失;显示、隐藏翻译图标 - document.addEventListener('mouseup', function (e) { - log('mouseup event:', e); - if (e.target == icon || (e.target.parentNode && e.target.parentNode == icon)) { // 点击了翻译图标 - e.preventDefault(); - return; - } - selected = window.getSelection().toString().trim(); // 当前选中文本 - log('click text:' + selected); - if (selected && icon.style.display == 'none') { // 显示翻译图标 - log('show icon'); - log(selected + ' | ' + e.pageX + ' | ' + e.pageY); - icon.style.top = e.pageY + 8 + 'px'; - icon.style.left = e.pageX + 4 + 'px'; - icon.style.display = 'block'; - // 兼容部分 Content Security Policy - icon.style.position = 'absolute'; - icon.style.zIndex = '2147483647'; - } else if (!selected) { // 隐藏翻译图标 - log('hide icon:mouseup'); - hideIcon(); - } - }); - // 选中变化事件:当点击已经选中的文本的时候,隐藏翻译图标(此时浏览器动作是:选中的文本已经取消选中了) - document.addEventListener('selectionchange', function (e) { - log('selectionchange event:', e); - log('selectionchange:' + window.getSelection().toString()); - if (!window.getSelection().toString().trim()) { - log('hide icon:selectionchange'); - hideIcon(); - } - }); + document.addEventListener('mouseup', showIcon); + // 选中变化事件 + document.addEventListener('selectionchange', showIcon); + document.addEventListener('touchend', showIcon); // 内容面板滚动事件 content.addEventListener('scroll', function (e) { if (content.scrollHeight - content.scrollTop === content.clientHeight) { @@ -771,6 +748,47 @@ engineActivateHide(); icon.querySelector('img[icon-id="' + engineId + '"]').setAttribute('activate', 'activate'); } + /**显示 icon*/ + function showIcon(e) { + log('showIcon event:', e); + var offsetX = 4; // 横坐标翻译图标偏移 + var offsetY = 8; // 纵坐标翻译图标偏移 + // 更新翻译图标 X、Y 坐标 + if (e.pageX && e.pageY) { // 鼠标 + log('mouse pageX/Y'); + pageX = e.pageX; + pageY = e.pageY; + } + if (e.changedTouches) { // 触屏 + if (e.changedTouches.length > 0) { // 多点触控选取第 1 个 + log('touch pageX/Y'); + pageX = e.changedTouches[0].pageX; + pageY = e.changedTouches[0].pageY; + // 触屏修改翻译图标偏移(Android、iOS 选中后的动作菜单一般在当前文字顶部,翻译图标则放到底部) + offsetX = -26; // 单个翻译图标块宽度 + offsetY = 16 * 3; // 一般字体高度的 3 倍,距离系统自带动作菜单、选择光标太近会导致无法点按 + } + } + log('selected:' + selected + ', pageX:' + pageX + ', pageY:' + pageY) + if (e.target == icon || (e.target.parentNode && e.target.parentNode == icon)) { // 点击了翻译图标 + e.preventDefault(); + return; + } + selected = window.getSelection().toString().trim(); // 当前选中文本 + log('selected:' + selected + ', icon display:' + icon.style.display); + if (selected && icon.style.display != 'block' && pageX && pageY) { // 显示翻译图标 + log('show icon'); + icon.style.top = pageY + offsetY + 'px'; + icon.style.left = pageX + offsetX + 'px'; + icon.style.display = 'block'; + // 兼容部分 Content Security Policy + icon.style.position = 'absolute'; + icon.style.zIndex = zIndex; + } else if (!selected) { // 隐藏翻译图标 + log('hide icon'); + hideIcon(); + } + } /**隐藏 icon*/ function hideIcon() { icon.style.display = 'none'; @@ -778,6 +796,8 @@ content.style.display = 'none'; engineId = ''; engineTriggerTime = 0; + pageX = 0; + pageY = 0; engineActivateHide(); audioCache = {}; engineResult = {}; diff --git a/translate/translate-dictionary.png b/translate/translate-dictionary.png index 82e4f38..616ee6b 100644 Binary files a/translate/translate-dictionary.png and b/translate/translate-dictionary.png differ diff --git a/translate/translate.js b/translate/translate.js index 79ce713..0c1d1f4 100644 --- a/translate/translate.js +++ b/translate/translate.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Translate // @namespace http://tampermonkey.net/ -// @version 6.3 +// @version 6.4 // @description 划词翻译调用“金山词霸、有道词典(有道翻译)、Google Translate(谷歌翻译)、沪江小D、搜狗翻译、必应词典(必应翻译)、Microsoft Translator(必应在线翻译)、海词词典、百度翻译、Oxford Learner's Dictionaries、Oxford Dictionaries、Merriam-Webster、PDF 划词翻译、Google Search、Bing Search(必应搜索)、百度搜索、Wikipedia Search(维基百科搜索)”网页翻译 // @author https://github.com/barrer // @match http://*/* @@ -19,15 +19,16 @@ // Your code here... /**样式*/ var style = document.createElement('style'); + var zIndex = '2147473647'; // 渲染图层 style.textContent = ` :host{all:unset!important} :host{all:initial!important} *{word-wrap:break-word!important} - img{cursor:pointer;display:inline-block;width:16px;height:16px;border:1px solid #dfe1e5;border-radius:4px;background-color:rgba(255,255,255,1);padding:2px;margin:0;margin-right:5px;box-sizing:content-box;vertical-align:middle} + img{cursor:pointer;display:inline-block;width:20px;height:20px;border:1px solid #dfe1e5;border-radius:4px;background-color:rgba(255,255,255,1);padding:2px;margin:0;margin-right:5px;box-sizing:content-box;vertical-align:middle} img:last-of-type{margin-right:auto} img:hover{border:1px solid #f90} img[is-more]{display:none} - tr-icon{display:none;position:absolute;padding:0;margin:0;cursor:move;background:transparent;box-sizing:content-box;font-size:13px;text-align:left;border:0;color:black;z-index:2147473647} + tr-icon{display:none;position:absolute;padding:0;margin:0;cursor:move;background:transparent;box-sizing:content-box;font-size:13px;text-align:left;border:0;color:black;z-index:${zIndex}} `; // iframe 工具库 var iframe = document.createElement('iframe'); @@ -336,8 +337,10 @@ }); }); log('hostCustomMap:', hostCustomMap); - // 翻译图标 - var icon = document.createElement('tr-icon'); + var icon = document.createElement('tr-icon'), // 翻译图标 + selected, // 当前选中文本 + pageX, // 图标显示的 X 坐标 + pageY; // 图标显示的 Y 坐标 // 绑定图标拖动事件 var iconDrag = new Drag(icon); // 翻译引擎添加到图标 @@ -409,36 +412,11 @@ e.preventDefault(); } }); - // 选中变化事件:当点击已经选中的文本的时候,隐藏翻译图标(此时浏览器动作是:选中的文本已经取消选中了) - document.addEventListener("selectionchange", function () { - log('selectionchange:' + window.getSelection().toString()); - if (!window.getSelection().toString().trim()) { - hideIcon(); - } - }); // 鼠标事件:防止选中的文本消失;显示、隐藏翻译图标 - document.addEventListener('mouseup', function (e) { - if (e.target == icon || (e.target.parentNode && e.target.parentNode == icon)) { // 点击了翻译图标 - e.preventDefault(); - return; - } - var text = window.getSelection().toString().trim(); - log('click text:' + text); - log(e); - if (text && icon.style.display == 'none') { - log('show icon'); - log(text + ' | ' + e.pageX + ' | ' + e.pageY); - icon.style.top = e.pageY + 8 + 'px'; - icon.style.left = e.pageX + 4 + 'px'; - icon.style.display = 'block'; - // 兼容部分 Content Security Policy - icon.style.position = 'absolute'; - icon.style.zIndex = '2147473647'; - } else if (!text) { - log('hide icon'); - hideIcon(); - } - }); + document.addEventListener('mouseup', showIcon); + // 选中变化事件 + document.addEventListener('selectionchange', showIcon); + document.addEventListener('touchend', showIcon); /**日志输出*/ function log() { var debug = false; @@ -584,9 +562,52 @@ iconDrag.unsetMouseMove(); } } + /**显示 icon*/ + function showIcon(e) { + log('showIcon event:', e); + var offsetX = 4; // 横坐标翻译图标偏移 + var offsetY = 8; // 纵坐标翻译图标偏移 + // 更新翻译图标 X、Y 坐标 + if (e.pageX && e.pageY) { // 鼠标 + log('mouse pageX/Y'); + pageX = e.pageX; + pageY = e.pageY; + } + if (e.changedTouches) { // 触屏 + if (e.changedTouches.length > 0) { // 多点触控选取第 1 个 + log('touch pageX/Y'); + pageX = e.changedTouches[0].pageX; + pageY = e.changedTouches[0].pageY; + // 触屏修改翻译图标偏移(Android、iOS 选中后的动作菜单一般在当前文字顶部,翻译图标则放到底部) + offsetX = -26; // 单个翻译图标块宽度 + offsetY = 16 * 3; // 一般字体高度的 3 倍,距离系统自带动作菜单、选择光标太近会导致无法点按 + } + } + log('selected:' + selected + ', pageX:' + pageX + ', pageY:' + pageY) + if (e.target == icon || (e.target.parentNode && e.target.parentNode == icon)) { // 点击了翻译图标 + e.preventDefault(); + return; + } + selected = window.getSelection().toString().trim(); // 当前选中文本 + log('selected:' + selected + ', icon display:' + icon.style.display); + if (selected && icon.style.display != 'block' && pageX && pageY) { // 显示翻译图标 + log('show icon'); + icon.style.top = pageY + offsetY + 'px'; + icon.style.left = pageX + offsetX + 'px'; + icon.style.display = 'block'; + // 兼容部分 Content Security Policy + icon.style.position = 'absolute'; + icon.style.zIndex = zIndex; + } else if (!selected) { // 隐藏翻译图标 + log('hide icon'); + hideIcon(); + } + } /**隐藏 icon*/ function hideIcon() { icon.style.display = 'none'; + pageX = 0; + pageY = 0; icon.querySelectorAll('img[is-more]').forEach(function (ele) { ele.style.display = 'none'; }); diff --git a/translate/translate.png b/translate/translate.png index b46fe91..074b92c 100644 Binary files a/translate/translate.png and b/translate/translate.png differ