摘要:谷歌是通過(guò)來(lái)實(shí)現(xiàn)這個(gè)組件的,比較復(fù)雜谷歌的工具加載文件和截圖文件有興趣的同學(xué)可以看一下。高亮區(qū)域核心部分截圖搞定了,接下來(lái)就是高亮區(qū)域了。
幾乎所有的APP應(yīng)用包括Web應(yīng)用都需要一個(gè)意見(jiàn)反饋,這樣才能了解用戶對(duì)產(chǎn)品的意見(jiàn)和建議,以便于不斷提升完善自己的產(chǎn)品。目前的反饋組件一般有兩種,一種是打開(kāi)一個(gè)反饋?lái)?yè)面填寫(xiě)表單,另一種則是通過(guò)彈窗來(lái)完成,相比較而言第二種更加方便,而且更加容易組件化。
國(guó)內(nèi)比較典型的有像知乎,百度這樣類型的反饋組件
國(guó)外則有谷歌的
由于本人比較喜歡谷歌的material design,而且谷歌的反饋組件功能也比較齊全,仿照谷歌的組件寫(xiě)一個(gè)自己的通用組件。
下面是PC和手機(jī)上的效果圖:
demo演示
項(xiàng)目github地址
首先根據(jù)谷歌的反饋插件分析需要實(shí)現(xiàn)哪些功能,這個(gè)組件在很多谷歌頁(yè)面中都會(huì)出現(xiàn),谷歌搜索頁(yè)出現(xiàn)的位置是底部。
根據(jù)實(shí)際操作能知道這個(gè)組件至少要包含這些功能:
截取當(dāng)前屏幕;
能編輯頁(yè)面的高亮或者遮擋區(qū)域;
可以適應(yīng)pc于手機(jī);
知道功能后就能一步步開(kāi)始實(shí)現(xiàn)它了。谷歌是通過(guò)iframe來(lái)實(shí)現(xiàn)這個(gè)組件的,比較復(fù)雜
谷歌的feedback工具加載文件:load.js和截圖文件screenshot.min.js有興趣的同學(xué)可以看一下。
這里使用自己的思路,使用現(xiàn)成模塊來(lái)簡(jiǎn)單實(shí)現(xiàn)這個(gè)功能。
需要獲取當(dāng)前屏幕內(nèi)容,第一時(shí)間反應(yīng)是使用canvas了,先把dom元素畫(huà)到canvas如何再生成圖片,幸好有一個(gè)牛掰的模塊叫做html2canvas,它可以將指定的DOM元素繪制到canvas上。
安裝html2canvas
npm i html2canvas
html2canvas 1.0.0版本中可以使用作者提供的html2canvas-proxy模塊來(lái)實(shí)現(xiàn)跨越資源代理。具體配置可以參考文檔。順便提一下,之前0.5.0版本中,html2canvas提供的代理方便不太好使,解決的方法是自己?jiǎn)?dòng)一個(gè)處理服務(wù),在頁(yè)面中遇到跨域圖片資源,使用服務(wù)將圖片轉(zhuǎn)成base64格式然后回填到圖片src屬性上,這樣來(lái)實(shí)現(xiàn)跨越圖片截圖。
如果頁(yè)面上不僅僅有圖片還有視頻該怎么辦呢,怎么截取視頻圖片呢?html2canvas是不支持截取video標(biāo)簽內(nèi)容的,但是html2canvas截圖時(shí)可以渲染元素的背景圖片。那么如果可以獲取視頻當(dāng)前播放的幀,把這一幀作為video標(biāo)簽的background,html2canvas就能讀取到了。
如何讀取video的幀,這個(gè)canvas的drawImage()方法就能做到(注意不能獲取跨域的視頻資源)。
let video = videoItem[0]; if(!video.style.backgroundImage) { let w = $(video).width(); let h = $(video).height(); $(video).after(""); let canvas = $(video).next("canvas").css({display: "none"}); let ctx = canvas.get(0).getContext("2d"); ctx.drawImage(video, 0, 0, w, h); try { video.style.backgroundImage = "url("+ canvas.get(0).toDataURL("image/png") +")"; }catch (e) { console.log(e) }finally { canvas.remove(); } }
做完這些準(zhǔn)備就可以開(kāi)始時(shí)截圖了
html2canvas(document.body, { proxy: this.props.proxy || "", width: window.innerWidth, height: window.innerHeight, x: document.documentElement.scrollLeft || document.body.scrollLeft, y: document.documentElement.scrollTop || document.body.scrollTop, }).then((canvas) => { let src = canvas.toDataURL("image/png"); ... }).catch((e) => { });
注意:html2canvas v1.0.0使用promise,v0.5.0采用的是回調(diào)函數(shù)。
2.高亮區(qū)域核心部分截圖搞定了,接下來(lái)就是高亮區(qū)域了。高亮分為兩部分:
鼠標(biāo)放在頁(yè)面上識(shí)別當(dāng)前鼠標(biāo)是在哪個(gè)DOM元素上然后將這個(gè)DOM元素高亮,給用戶提供一個(gè)快捷選區(qū)方式。
用戶自己用鼠標(biāo)選區(qū)一個(gè)高亮區(qū)域。
第二點(diǎn)很容易實(shí)現(xiàn),只有鼠標(biāo)按下時(shí)記錄點(diǎn)擊點(diǎn)位置,然后隨著鼠標(biāo)移動(dòng)計(jì)算與初始點(diǎn)位置的差值就能得到一個(gè)區(qū)域了,那么怎么識(shí)別鼠標(biāo)是放在哪個(gè)DOM元素上呢?有一個(gè)Web API可以輕松實(shí)現(xiàn)這個(gè)功能那就是elementsFromPoint。
elementsFromPoint() 方法可以獲取到當(dāng)前視口內(nèi)指定坐標(biāo)處,由里到外排列的所有元素。
使用方法很簡(jiǎn)單,只要給x,y坐標(biāo)就行了。
var elements = document.elementsFromPoint(x, y);
這個(gè)方法返回的是一個(gè)包含當(dāng)前鼠標(biāo)所在位置的DOM元素?cái)?shù)組。元素在數(shù)組中的位置與元素的z軸位置和元素包含關(guān)系有關(guān),z軸越大位置越靠前,子元素比父元素靠前。
如果是這個(gè)結(jié)構(gòu)那么當(dāng)鼠標(biāo)在img標(biāo)簽上document.elementsFromPoint(x, y)返回的值是這樣的
[img, p, div]
得到元素后那么后續(xù)的操作就簡(jiǎn)單了,在一個(gè)半透明的黑色區(qū)域上摳出透明部分,顯然css是實(shí)現(xiàn)不了的,那么就上canvas吧。
首先要畫(huà)一個(gè)半透明的遮罩:
let canvas = this.refs.canvas; if (!this.ctx) { this.ctx = canvas.getContext("2d"); } let docWidth = document.body.clientWidth, docHeight = document.body.clientHeight; if(docHeight < window.innerHeight) { docHeight = window.innerHeight; } canvas.width = docWidth; canvas.height = docHeight; canvas.style.width = docWidth; canvas.style.height = docHeight; this.ctx.fillStyle = "rgba(0,0,0,0.3)"; this.ctx.fillRect(0, 0, docWidth, docHeight);
準(zhǔn)備完遮罩就可以開(kāi)始扣圖了。通過(guò)elementsFromPoint的到元素后使用getBoundingClientRect()方法得到元素的位置信息。 getBoundingClientRect()用于獲得頁(yè)面中某個(gè)元素的左,上,右和下分別相對(duì)瀏覽器視窗的位置以及元素寬高。
寬高,位置信息都有了就可以開(kāi)始繪制了:
this.ctx.lineWidth = "5"; this.ctx.strokeStyle = "#FEEA4E"; this.ctx.rect(x, y, width, height); this.ctx.stroke(); this.ctx.clearRect(x, y, width, height);
同理如果不是高亮而是遮擋只要把清除區(qū)域換成繪制一個(gè)半透明黑色區(qū)域就可以了。
要注意每次畫(huà)新區(qū)域時(shí)要清除上次繪制的內(nèi)容所以每次都得初始化一次canvas內(nèi)容。
由于手機(jī)頁(yè)面中的反饋界面與PC差距太大所以不能采用同一套模板,通過(guò)一個(gè)state來(lái)區(qū)分該渲染那種類型。在組件willMount的時(shí)候判斷設(shè)備類型
let device = "pc"; let ua = navigator.userAgent; let ipad = ua.match(/(iPad).*OSs([d_]+)/), isIphone = !ipad && ua.match(/(iPhonesOS)s([d_]+)/), isAndroid = ua.match(/(Android)s+([d.]+)/), isMobile = isIphone || isAndroid; if (isMobile) { device = "mobile"; this.setState({ device: device, }); }
得到設(shè)備后根據(jù)設(shè)備進(jìn)行渲染
{ this.state.device == "pc"?: }
需要注意的是有些手機(jī)瀏覽器在打開(kāi)輸入法后會(huì)導(dǎo)致頁(yè)面窗口變化所以需要監(jiān)聽(tīng)窗口變化去做適配調(diào)整
2.使用 1.安裝:使用npm
npm install react-googlefeedback --save-dev2.使用
react中:
import React from "react"; import ReactDOM from "react-dom"; import Feedback from "react-googlefeedback"; import "react-googlefeedback/dist/style.css"; const license = `如出于法律原因需要請(qǐng)求更改內(nèi)容,請(qǐng)前往 法律幫助 頁(yè)面。系統(tǒng)可能已將部分 帳號(hào)和系統(tǒng)信息 發(fā)送給Google。我們將根據(jù)自己的 隱私權(quán)政策和服務(wù)條款 使用您提供的信息幫助解決技術(shù)問(wèn)題和改進(jìn)我們的服務(wù)。`; class Page extends React.Component { constructor() { super(); this.state = { open: false, } } open() { this.setState({ open: true, }) } cancel() { this.setState({ open: false, }) } send(data) { console.log(data) } render() { return ({ this.state.open?) } } ReactDOM.render(:null } , document.getElementById("main"));
在頁(yè)面中引入js文件使用:
3.參數(shù)說(shuō)明
react 組件:
參數(shù) | 功能 | 類型 | 是否必填 |
---|---|---|---|
theme | 設(shè)置組件主題顏色 | string | ? 默認(rèn)值 #3986FF |
cancel | 取消按鈕處理函數(shù) | function | ? |
send | 發(fā)送按鈕處理函數(shù),會(huì)傳回收集的數(shù)據(jù) | function | ? |
license | 協(xié)議內(nèi)容 | html字符串 | ? 默認(rèn)值為谷歌的隱私條款協(xié)議 |
proxy | 代理地址,如果頁(yè)面中存在跨域資源可以設(shè)置這個(gè)值 | string | ? 默認(rèn)空值 |
頁(yè)面中直接引用:
參數(shù) | 功能 | 類型 | 是否必填 |
---|---|---|---|
container | 組件容器元素 | element | ? |
trigger | 用于觸發(fā)組件打開(kāi)的元素 | element | ? |
theme | 設(shè)置組件主題顏色 | string | ? 默認(rèn)值 #3986FF |
license | 協(xié)議內(nèi)容 | html字符串 | ? 默認(rèn)值為谷歌的隱私條款協(xié)議 |
proxy | 代理地址,如果頁(yè)面中存在跨域資源可以設(shè)置這個(gè)值 | string | ? 默認(rèn)空值 |
send | 發(fā)送按鈕處理函數(shù),會(huì)傳回收集的數(shù)據(jù) | function | ? |
需要啟動(dòng)一個(gè)服務(wù)用于代理。
首先安裝html2canvas-proxy
npm install html2canvas-proxy --save
在node中使用代理
var proxy = require("html2canvas-proxy"); var express = require("express"); var app = express(); app.use("/", proxy());
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/95083.html
摘要:實(shí)現(xiàn)不定期更新技巧前端掘金技巧,偶爾更新。統(tǒng)一播放效果實(shí)現(xiàn)打字效果動(dòng)畫(huà)前端掘金前端開(kāi)源項(xiàng)目周報(bào)前端掘金由出品的前端開(kāi)源項(xiàng)目周報(bào)第四期來(lái)啦。 Web 推送技術(shù) - 掘金騰訊云技術(shù)社區(qū)-掘金主頁(yè)持續(xù)為大家呈現(xiàn)云計(jì)算技術(shù)文章,歡迎大家關(guān)注! 作者:villainthr 摘自 前端小吉米 伴隨著今年 Google I/O 大會(huì)的召開(kāi),一個(gè)很火的概念--Progressive Web Apps ...
摘要:官網(wǎng)地址聊天機(jī)器人插件開(kāi)發(fā)實(shí)例教程一創(chuàng)建插件在系統(tǒng)技巧使你的更加專業(yè)前端掘金一個(gè)幫你提升技巧的收藏集。我會(huì)簡(jiǎn)單基于的簡(jiǎn)潔視頻播放器組件前端掘金使用和實(shí)現(xiàn)購(gòu)物車場(chǎng)景前端掘金本文是上篇文章的序章,一直想有機(jī)會(huì)再次實(shí)踐下。 2道面試題:輸入U(xiǎn)RL按回車&HTTP2 - 掘金通過(guò)幾輪面試,我發(fā)現(xiàn)真正那種問(wèn)答的技術(shù)面,寫(xiě)一堆項(xiàng)目真不如去刷技術(shù)文章作用大,因此刷了一段時(shí)間的博客和掘金,整理下曾經(jīng)被...
摘要:你可以利用漸變和填充等功能來(lái)創(chuàng)建簡(jiǎn)單的形狀,并且可以訪問(wèn)幾乎所有蘋(píng)果默認(rèn)提供的控件。可以幫你生成蘋(píng)果指導(dǎo)方針?biāo)f(shuō)的各種分辨率圖標(biāo),包括設(shè)備和非設(shè)備。是一個(gè)用來(lái)管理蘋(píng)果推送通知的腳本。 這里推薦20個(gè)可以幫你簡(jiǎn)化iOS app開(kāi)發(fā)流程的工具。很多開(kāi)發(fā)者都使用過(guò)這些工具,涉及原型和設(shè)計(jì)、編程、測(cè)試以及最后的營(yíng)銷,基本上涵蓋了整個(gè)開(kāi)發(fā)過(guò)程。 ? 原型和設(shè)計(jì) 有了一個(gè)很好的創(chuàng)意后,你要做...
閱讀 1057·2021-11-18 10:02
閱讀 1314·2021-09-23 11:22
閱讀 2617·2021-08-21 14:08
閱讀 1643·2019-08-30 15:55
閱讀 1728·2019-08-30 13:45
閱讀 3169·2019-08-29 16:52
閱讀 3099·2019-08-29 12:18
閱讀 1643·2019-08-26 13:36