ilter 是 CSS 中一種強大的圖像過濾效果,可以應用各種濾鏡來改變圖像的外觀。它允許我們對圖像進行各種各樣的處理,如模糊、亮度調節、對比度調節、色彩轉換等。
在這篇博客中,我們將深入探討 CSS filter 的各種濾鏡效果,并提供一些示例來幫助你更好地理解和應用它們。
CSS filter 提供了多種濾鏡類型,包括:
除了以上基本濾鏡類型,filter 還支持一些組合濾鏡,如blur()和opacity()的組合,以及一些高級濾鏡,如drop-shadow()和filter()。
/* 模糊濾鏡 */
.filter-blur {
filter: blur(5px);
}
/* 亮度濾鏡 */
.filter-brightness {
filter: brightness(0.5);
}
/* 對比度濾鏡 */
.filter-contrast {
filter: contrast(2);
}
/* 灰度濾鏡 */
.filter-grayscale {
filter: grayscale(1);
}
/* 色調旋轉濾鏡 */
.filter-hue-rotate {
filter: hue-rotate(90deg);
}
/* 反轉濾鏡 */
.filter-invert {
filter: invert(1);
}
/* 透明度濾鏡 */
.filter-opacity {
filter: opacity(0.5);
}
/* 飽和度濾鏡 */
.filter-saturate {
filter: saturate(2);
}
/* 褐色濾鏡 */
.filter-sepia {
filter: sepia(1);
}
/* 組合濾鏡 */
.filter-blur-opacity {
filter: blur(5px) opacity(0.5);
}
/* 高級濾鏡 */
.filter-drop-shadow {
filter: drop-shadow(10px 10px 10px rgba(0, 0, 0, 0.5));
}
.filter-filter {
filter: url("filter.svg#filter");
}
在上面的示例中,我們定義了不同的 CSS 類,每個類應用了不同的 CSS filter 濾鏡效果。通過將這些類應用到 HTML 元素上,可以輕松地實現各種圖像過濾效果。
在上面的示例中,我們將不同的 CSS filter 濾鏡類應用到 HTML 圖像元素上,通過 src 屬性指定了要應用濾鏡的圖像。你可以替換為你自己的圖像文件路徑。
請注意,filter 濾鏡在不同的瀏覽器和版本中可能存在兼容性問題。某些高級濾鏡,如filter(),可能需要特定的圖像文件格式(如 SVG)來正確應用。在實際應用中,建議進行兼容性測試,并根據需要提供適當的回退樣式。
希望這篇博客能夠幫助你更好地理解和應用 CSS filter 濾鏡。如果你有任何進一步的問題,請隨時提問。
ilter的攔截的工作過程:
馬克- to-win:馬克 java社區:防盜版實名手機尾號: 73203。
馬克-to-win:如下圖所示:假設我們的工作場景由三個Filter組成。用戶從客戶瀏覽器發起請求,訪問服務器端某一個html,jsp或 Servlet。如果在Web.xml里,正好配置這個目標文件是在Filter1,Filter2,Filter3的控制之下,則按照下圖順序,客戶的請求先經過Filter3,再經過Filter2,最后經過Filter1,到達所訪問的目標文件,之后再經過Filter1,Filter2,和 Filter3到達客戶瀏覽器。注意是兩次經過同一個Filter,而且順序正好相反。
Filter的代碼實現方法是什么
Filter的代碼實現方法:
馬克- to-win:馬克 java社區:防盜版實名手機尾號: 73203。
馬克-to-win:要想實現Filter,條件1)你的Filter類必須實現javax.servlet包中的Filter接口。條件2)你還需在 web.xml中加一段類似下面這樣的話,其中url-pattern指定了你的Filter要作用的目標,其中下例中的/*代表根目錄下的所有東西,包括html,jsp,servlet等。
<filter>
<filter-name>kkk</filter-name>
<filter-class>你的Filter類</filter-class>
</filter>
<filter-mapping>
<filter-name>kkk</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
篇幅有限更多請見擴展鏈接:http://www.mark-to-win.com/tutorial/jsp_6_FilterInterceptProcess.html
我們開發系統的時候,可能會接到這樣的需求:不要讓用戶復制頁面上的文字或者圖片,不要讓用戶調試我們的頁面,更甚至也不要讓用戶進行打印操作等等。
聽起來是不是讓人很頭大,這咋實現啊?這有必要嗎?這能禁住么?
如果你沒做過這些,或者沒接到過這樣的需求,那你也應該看到過某個網站做了一些這樣的措施。
既然要做,我們就得想方案,先來看看禁止復制都有哪些方法。
假設我們有這樣一段代碼:
<div style="padding-left: 56px;">
<textarea rows="5" cols="33"></textarea>
</div>
<pre>
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
接下來就通過這個例子來論述我們的方案:
x效果
這是一個css屬性,標識了元素及其子元素的文本不可被選中,因此設定之后,文本將不能夠被選中,因此也就不能復制:
<pre style="user-select: none;">
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
我們在這段文本上,加上這個樣式。
效果
可以看到,文字壓根就不能選擇,從鼠標形狀也能看出來。
由于用在進行復制操作的時候,會觸發copy事件,我們可以通過監聽它來做一些處理,使得復制的行為發生改變:
<div>
<div style="padding-left: 56px;">
<textarea rows="5" cols="33"></textarea>
</div>
<pre id="content">
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
</div>
<script>
let c=document.getElementById('content')
c.removeEventListener("copy", copyFilter)
c.addEventListener("copy", copyFilter)
function copyFilter(e) {
let cp=e.clipboardData || window.clipboardData
if(!cp) {
return
}
let text=window.getSelection().toString()
if(text) {
e.preventDefault()
cp.setData("text/plain", "你復制了一段魔法")
}
}
</script>
先獲取到我們要禁止復制的元素,然后給它添加一個copy的事件監聽,在添加監聽之前,要先移除一下,這樣是為了避免局部刷新的時候重復添加,然后我們通過copyFilter函數來對這次操作進行處理。
先獲取剪貼板對象,如果當前事件對象里不存在,那就從window里面取,然后我們通過getSelection再拿到選取的內容,因為我們對剪貼板對象進行修改,所以要阻止默認行為,然后把剪貼板的內容重新賦值,可以是示例中那樣的一段文字,也可以設置為空,甚至是任意其他內容,然后我們就可以看到產生的效果了:
效果
雖然能復制文本,但是由于我們攔截了復制操作,更改了它的行為,因此再粘貼的時候就變成了我們更改的樣子,也做到了禁止復制的功能。
這種方式對于使用快捷鍵或者右鍵的方式都是有效的。
這種情況主要是在可編輯區域,比如文本框、文本域、設置為contenteditable的元素等,用戶可以對文字進行剪切操作,雖然上面禁止了復制,但是剪切是另一個操作,不攔截的話還是相當于能復制出來。
copy和cut只是觸發的事件不同而已,但是它們都是執行相同的邏輯處理:
<div>
<div style="padding-left: 56px;">
<textarea rows="5" cols="33"></textarea>
</div>
<pre id="content" contenteditable>
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
</div>
<script>
let c=document.getElementById('content')
c.removeEventListener("cut", copyFilter)
c.addEventListener("cut", copyFilter)
function copyFilter(e) {
let cp=e.clipboardData || window.clipboardData
if(!cp) {
return
}
let text=window.getSelection().toString()
if(text) {
e.preventDefault()
cp.setData("text/plain", "你復制了一段魔法")
}
}
</script>
這里我為了方便,給元素添加了contenteditable屬性,讓它變成可編輯的,copyFilter函數沒有變化,我們只是添加了一個剪切事件的監聽,然后它們的處理函數都是copyFilter。看下效果:
效果
可以看到,首先我們對文字進行剪切,沒有出現預期的效果,這時因為我們在代碼里面對剪切進行了攔截,并阻止了它的默認行為,然后我們在粘貼的時候,文字也改變成我們設置的了。
雖然我們可以通過上面的幾種方法禁止在頁面上復制,但是用戶也可能開啟打印預覽模式,在這種情況下,也是可以進行復制的,我們要想對打印頁面進行一些控制,那么就要用到媒體查詢,先看下打印的樣子:
效果
雖然我們做了限制,但是在打印頁面沒有生效,現在我們針對這個場景更改一下代碼:
@media print {
html {
display: none;
}
}
通過添加上面這個樣式規則,我們能夠使頁面在打印的時候,內容隱藏起來,這樣就無法進行復制了:
效果
能夠看到,點擊打印的時候,預覽頁面一片空白,,這樣就禁止了在打印頁面進行復制的操作。當然了,你其實也可以設置其他的樣式屬性來做些控制,但要記住寫在打印的媒體查詢里面,只有這樣才會在打印頁面生效。
還有一種方式就是,通過設定一個偽元素,讓它全面覆蓋文本內容,這樣鼠標就不能選到實際的文本,改造一下代碼:
.content {
position: relative;
}
.content::before {
content: '';
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
}
<pre id="content" class="content">
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
現在就不能在元素上面選中文字了,不過用戶也可能還有一些操作,比如在內容區域外面ctrl+a全選,或者在外面拖動鼠標來全選,如果是這種情形,那么我們可以通過監聽鍵盤和鼠標事件來禁止全選等操作。
由于用戶有很多種操作的方式,鍵盤全選、鼠標全選、鍵盤右鍵、鼠標右鍵等等,我們如果窮舉的話,情況太多了,因此我們只監聽鼠標按下和抬起事件,以及鍵盤的按下事件:
document.removeEventListener("mousedown", haveSelect)
document.addEventListener("mousedown", haveSelect)
document.removeEventListener("mouseup", haveSelect)
document.addEventListener("mouseup", haveSelect)
document.removeEventListener("keydown", haveSelect)
document.addEventListener("keydown", haveSelect)
function haveSelect() {
window.getSelection().removeAllRanges()
}
主要就是在removeAllRanges方法上面,能夠在觸發上面事件的時候,將所選區域清空,也就是不管你選沒選,咋選的,反正就是你只要進行了操作,那我就那可能選擇的區域給你清空,這樣你就啥也干不了了。
效果
好,這樣就可以啦,無論怎么選,即使出現了選區,但是只要你再按了鼠標或者鍵盤,那么選區就會直接消失,就能達到不能復制的效果,因為你發現啥都做不了。
這里額外說明一下,對于事件的監聽,一定要用addEventListener來實現,因為它會將多個綁定的事件都添加上去,當觸發的時候就會按照綁定的順序進行執行,如果是用賦值的方式,那么后面的會覆蓋前面的,而且賦值的方式很容易被篡改,可以很輕松的讓你的綁定函數不能執行從而失效,而使用addEventListener就不會被人為覆蓋,只能通過綁定的函數句柄來手動移除,也就是說要移除的時候,必須使用跟綁定時使用同一個函數才行。
通過CSS的方式禁止復制,可以很容易的被用戶解除,只能是設置的稍微復雜一點,增加難度。而通過JS的方式禁止復制,也可以通過禁用頁面JavaScript代碼來解除,因此我們可以將內容通過js來渲染,這樣如果頁面禁用了js,那么內容也不會渲染。
對于禁止調試,主要是指用戶打開控制臺,控制臺也就是開發者工具,我為了方便稱之為控制臺,想要對頁面進行調試時,我們做一些處理,阻止這種行為,最大可能的攔截通過控制臺對系統的調試。
主要的方法有幾種,由于打開控制臺是瀏覽器提供的調試功能,因此我們沒法攔截打開操作,即使通過事件監聽不允許快捷鍵這樣做,但是也可以通過其他方式進行打開,因此我們的主要關注點就在于打開控制臺之后,我們能做哪些事情來限制用戶行為。
打開控制臺的快捷鍵主要有F12和ctrl+shift+i,我們先把這倆給禁用了:
document.removeEventListener("keydown", disableDevShortcut)
document.addEventListener("keydown", disableDevShortcut)
function disableDevShortcut(e) {
console.log(e)
if(e.keyCode===123) {
e.preventDefault()
}else if(e.keyCode===73 && e.ctrlKey && e.shiftKey) {
e.preventDefault()
}
}
這樣在使用這兩個快捷鍵的時候,頁面沒有任何反應,控制臺也不會喚起,因為我們阻止了它們的默認行為。
除了通過快捷鍵,還可以使用右鍵的方式,并點擊檢查也會調出控制臺。
效果
這種情景,我們可以通過禁止在頁面上使用右鍵的方式,來阻止打開控制臺:
document.removeEventListener("contextmenu", cancelContextmenu)
document.addEventListener("contextmenu", cancelContextmenu)
function cancelContextmenu(e) {
e.preventDefault()
}
現在就不能通過右鍵打開控制臺了,但是相應的整個右鍵功能也都不能使用了。
如果用戶最終打開了控制臺,比如通過在瀏覽器的更多功能中來打開的話,那么我就需要采取其他的措施,其中之一就是給代碼設置無限斷點,因為斷點只在控制臺打開的時候才會發生作用,從而不必擔心非調試模式下的程序正常運行。
無限斷點的主要思路就是利用定時器等手段,頻繁的觸發斷點效果,使得不能輕松的調試程序,先看下代碼:
;(()=> {
function breakDebugger() {
if(new checkDebugger().check) {
breakDebugger()
}
}
function checkDebugger() {
const now=new Date();
eval('(function () {debugger;false;})()')
const dur=Date.now() - now
if(dur < 5) {
return {check: false}
}else {
return {check: true}
}
}
setInterval(()=> {
eval('(function () {debugger;true;})()')
breakDebugger()
}, 500)
})()
我們利用一個立即執行的自執行函數,來使我們的代碼被封裝在一個固定塊內,不與其他部分有任何影響。
這里主要做了兩步:
第一步設置一個重復執行的定時器,其中包括了一個斷點和一個函數調用。
第二步通過函數來遞歸調用斷點,主要使用了實例化對象的方式和時間差的判斷。
這樣做的主要作用就是在設置無限斷點的同時,也能夠讓每次的斷點都是被重新生成的,看下效果,一目了然:
效果
發現沒有,我們通過這種方式,只要打開了控制臺,那么就會進入到無限斷點的循環中,使得不能做任何其他事情,而且每個斷點的生成都會開辟一個新的虛擬運行環境,這種情況下,只有關閉控制臺,才能結束斷點。
即使使用右鍵選擇Never parse here,也毫無作用,雖然可以通過Deactive breakpoints按鈕來徹底禁用斷點,就是下面這個按鈕:
按鈕
但是,如果這樣做的話,那么用戶也就同時失去了調試其他代碼的能力。
況且,我們接下來還會介紹其他的控制手段,可以配合著使用。
我們如果能有一種手段,可以知道用戶開啟了控制臺,換句話說只要控制臺被打開,就通知我們或者被我們監測到,那么我們就可以執行一些控制手段,這種效果肯定是很理想的,遺憾的是還沒有這種api暴露給我們去讓我們能夠這樣做。
不過我們可以通過其他的方式,利用既有的一些能力來實現這一點,這里我還是使用循環定時器,來不斷的去嗅探用戶是否開啟了控制臺,直接看代碼:
;(()=> {
setInterval(function() {
let foo=document.createElement('a')
let a1=+new Date()
console.table(foo)
let a2=+new Date()
if(a2 - a1 > 1) {
location.href='about:blank'
}
console.clear()
}, 500)
})()
同樣,通過一個自執行函數,我們開啟了一個循環定時器,然后在回調方法里面,我們就去實現上面的目標,也是分為了兩步:
第一步創建一個a元素,然后通過表格的形式將它打印出來,并記錄下消耗的時間。
第二步判斷耗時的長短來控制是否跳轉到空白頁,然后清空控制臺。
這種方式主要是利用了console.table的特性,它會將元素以表格的形式輸出到控制臺,大概就像下面的樣子:
效果
由于太多了,我就沒有全部截下來,如果沒有打開控制臺的話,使用console.table輸出我們創建的a標簽是很快的,有多快呢,就是js執行一條語句的速度,所以打印a1和a2的時間間隔非常短,幾乎為0,因為他們快到差不多是同時執行的,給大家打印看一下:
效果
我們先不打開控制臺,等輸出完畢再打開,很清楚的發現,我們沒打開控制臺的時候,輸出的a就是它標簽,而且時間間隔是0毫秒。
現在我們打開控制臺的時候刷新一下頁面,看看控制臺的輸出:
效果
這次就變成了以table的形式輸出a元素,而且它的耗時明顯增多,不再是0毫秒,而是耗費了10毫秒,雖然打開控制臺的時候多次刷新頁面,每次輸出的毫秒數是不同的,但是跟關閉控制臺的時候輸出的耗時差距非常明顯,因此我們就可以在這個上面做文章。
我在上面的代碼中假定了,只要是大于1毫秒的耗時,那就表示用戶打開了控制臺,然后我們就把頁面給跳轉到空白頁,當然了你也可以做一些任何你想做的操作,比如彈出一個提示,或者把body內容置空等等等等。
回到我們上面的代碼,看一下它實際發生的作用和帶給我們的效果:
效果
哈哈,古德古德,平時瀏覽一切正常,只要剛一打開控制臺,瞬間頁面就被跳轉走了,什么都干不了。這樣我們就通過這種方式,達到了限制打開控制臺的目的,也就是在當前頁沒法調試,一打開就跳轉。
這種辦法由于是繞路實現的,那么你可能會有疑問,它穩定嗎?會不會誤判,我可以對它絕對放心嗎?
理論上來說,通過輸出的執行時間是不太能精確掌握的,但是我們可以再做一些其他的措施來逼近真相:
;(()=> {
setInterval(function() {
let foo=document.createElement('a')
let a1=+new Date()
console.table(foo)
let a2=+new Date()
if(a2 - a1 > 1) {
let time=0
for(let i=0; i < 10; i++) {
let a1=+new Date()
console.table(foo)
let a2=+new Date()
time +=a2 - a1
}
if(time > 20) {
location.href='about:blank'
}
}
console.clear()
}, 500)
})()
我又改造了一下判斷的邏輯,當發現輸出耗時為2毫秒甚至更多的時候,我立馬再進行一次真偽判斷,也就是說,萬一由于其他的影響,導致我第7行的代碼誤判了,那么我再同步執行一個循環,連續輸出10次,把他們的耗時總和計算出來,然后判斷是否大于20毫秒,如果還是耗時過高的話,那么就可以非常肯定的知道用戶是打開了控制臺,這個時候就可以放心的做一些處理了。
其實限制用戶行為的方法有很多很多,上面列出了一些主要的,多種方法還是要結合著使用。你也可以自由發揮,多使用一些其他的手段,也會增加用戶復制或者調試的難度,比如防止用戶重寫console的方法,或者清除所有定時器等。
甚至也可以將你的內容繪制到canvas上面來防止復制,多加一些js的處理工作,防止禁用js的時候,我們的代碼不生效,只有在js可用的時候再去渲染內容等。也可以在綜合考慮的情況下加上代碼混淆、代碼加密等措施。
話說回來,大家都是同路人,何必相互為難,哈哈哈,不過提這個需求的人也著實會為難我們,既然提了那就盡力去做,能做到什么程度,只能說是盡量做到極致。
希望上面的內容能夠幫助到你,也希望能夠對你有所啟發。
謝謝
*請認真填寫需求信息,我們會在24小時內與您取得聯系。