由canvas實現btn效果有感

愛程式設計的李先森發表於2018-12-10

前言

最近專案上有個工業自動化的需求,做一個按鈕,有按下彈起效果,你肯定會說這不是so easy嗎?是的,沒錯,用css,分分鐘寫一個,不過我們是做的拓撲圖,用的canvas,因此我就想css畫圖也是gpu繪製,css能做的效果,我大canvas理應也能實現,於是我試著用css的實現方式做了一個canvas版的按鈕效果。

css3版的立體按鈕效果

想想我們用css怎麼做一個按鈕,首先我們就能想到按鈕需要有陰影,我們可以使用box-shadow,他的語法如下:

/* x偏移量 | y偏移量 | 陰影顏色 */ box-shadow: 60px -16px teal;

/* x偏移量 | y偏移量 | 陰影模糊半徑 | 陰影顏色 */ box-shadow: 10px 5px 5px black;

/* x偏移量 | y偏移量 | 陰影模糊半徑 | 陰影擴散半徑 | 陰影顏色 */ box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);

/* 插頁(陰影向內) | x偏移量 | y偏移量 | 陰影顏色 */ box-shadow: inset 5em 1em gold;

/* 任意數量的陰影,以逗號分隔 */ box-shadow: 3px 3px red, -1em 0 0.4em olive;

/* 全域性關鍵字 */ box-shadow: inherit; box-shadow: initial; box-shadow: unset;

首先我們定義一個btn類,先畫出一個按鈕的基礎樣式

.btn {
    border-radius: 5px;
    padding: 15px 25px;
    font-size: 22px;
    text-decoration: none;
    margin: 20px;
    color: #fff;
    position: relative;
    display: inline-block;
}
複製程式碼

然後我們定義一下按鈕的背景色

.blue {
    background-color: #55acee;
}
複製程式碼

這是我們的按鈕基礎樣式就完成了,如下所示:

F3oaF0.png

接下來我們在來為按鈕新增陰影

.btn:active {
    transform: translate(0px, 5px);
    -webkit-transform: translate(0px, 5px);
    box-shadow: 0px 1px 0px 0px;
}

.blue {
    background-color: #55acee;
    box-shadow: 0px 5px 0px 0px #3C93D5;
}
複製程式碼

給y方向5px的偏移,就會有陰影的效果,有點立體感覺,然後按下按鈕之後我們還要去掉陰影,然後按鈕的位置要向下偏移響應的5px,這樣效果如下所示:

F3oWY6.gif

canvas版的按鈕效果

css的按鈕我們已經知道了其實也就是利用box-shadow繪製陰影來實現按鈕的樣式,那麼canvas我們怎麼繪製陰影呢?翻一翻萬能的mdn

shadowOffsetX = float

shadowOffsetXshadowOffsetY用來設定陰影在 X 和 Y 軸的延伸距離,它們是不受變換矩陣所影響的。負值表示陰影會往上或左延伸,正值則表示會往下或右延伸,它們預設都為 0

shadowOffsetY = float

shadowOffsetX 和 shadowOffsetY用來設定陰影在 X 和 Y 軸的延伸距離,它們是不受變換矩陣所影響的。負值表示陰影會往上或左延伸,正值則表示會往下或右延伸,它們預設都為 0

shadowBlur = float

shadowBlur 用於設定陰影的模糊程度,其數值並不跟畫素數量掛鉤,也不受變換矩陣的影響,預設為 0

shadowColor = color

shadowColor 是標準的 CSS 顏色值,用於設定陰影顏色效果,預設是全透明的黑色。

第一步我們先繪製矩形,繪製矩形我們使用moveTo和lineTo,因為按鈕一般是帶有圓角的,所以我們封裝一個繪製圓角矩形的

createPath: function (x, y, width, height, radius) {
            this.ctx.moveTo(x, y + radius);
            this.ctx.lineTo(x, y + height - radius);
            this.ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
            this.ctx.lineTo(x + width - radius, y + height);
            this.ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
            this.ctx.lineTo(x + width, y + radius);
            this.ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
            this.ctx.lineTo(x + radius, y);
            this.ctx.quadraticCurveTo(x, y, x, y + radius);
        },
複製程式碼

圓角矩形繪製完成後我們在接著繪製陰影,

setShadow: function (xoffset, yoffset) {
    var style = this.style;
    this.ctx.shadowOffsetX = xoffset || 0;
    this.ctx.shadowOffsetY = yoffset || 5;
    this.ctx.shadowBlur = 0;
    this.ctx.shadowColor = style.shadowColor;
},
複製程式碼

陰影繪製完後我們還要繪製文字,畢竟是canvas,繪製文字不像css那麼方便,我們需要計算文字寬度來定位,程式碼如下:

 drawText: function () {
     var xoffset = this.ctx.measureText(this.text).width;
     var x = this.x,
         y = this.y;
     if (this.state === 'active') {
         y = y + 5;
     }
     this.ctx.save();
     this.ctx.beginPath();
     this.ctx.font = "30px Micosoft yahei";
     this.ctx.fillStyle = this.fontColor;
     this.ctx.textBaseline = 'middle';
     this.ctx.textAlign = 'center';
     this.ctx.fillText(this.text, x + (this.width - xoffset) / 2 + 10, y + (this.height - 22) / 2 + 5, this.width);
     this.ctx.closePath();
     this.ctx.restore();
 },

複製程式碼

另附textAlign和textBaseLine的說明:

textAlign:

由canvas實現btn效果有感

textBaseLine:

textBaseLine

這樣效果就基本大功告成了

由canvas實現btn效果有感

canvas事件可以具體看我的程式碼和我以前的部落格

canvas發光效果

上面介紹瞭如何繪製canvas陰影立體按鈕,接下來我們來實現試試更進一步的效果,多層陰影疊加效果,在css中我們的box-shadow可以疊加多層陰影效果,並且通過逗號分隔。那麼我們canvas是不是也可以同樣實現呢?我們知道canvas是基於狀態的,我們如果要繪製多層陰影疊加,那麼就需要儲存每次繪製的陰影,層疊在一起,那麼我們改下程式碼,每畫一層陰影就需要儲存一次canvas狀態:

drawText: function (shadowx, shadowy, blur, shadowColor) {
            var xoffset = this.ctx.measureText(this.text).width;
            var x = this.x,
                y = this.y;
            this.ctx.save();
            this.ctx.beginPath();
            this.setShadow(shadowx, shadowy, blur, shadowColor);
            this.ctx.font = "300px Micosoft yahei";
            this.ctx.fillStyle = this.fontColor;
            this.ctx.textBaseline = 'middle';
            this.ctx.textAlign = 'center';
            this.ctx.fillText(this.text, x + (this.width - xoffset) / 2 + 10, y + (this.height - 22) / 2 + 5, this.width);
            this.ctx.closePath();
            this.ctx.restore();
        },
        
        setShadow: function (shadowx, shadowy, blur, shadowColor) {
            this.ctx.shadowOffsetX = shadowx || 0;
            this.ctx.shadowOffsetY = shadowy || 0;
            this.ctx.shadowBlur = blur || 10;
            this.ctx.shadowColor = shadowColor;
        },
複製程式碼

效果如下:

F8GQ4U.gif

不太完美,有時間在繼續優化,動畫的發光還是不太柔和,與css比效果還是有點欠缺,以後再優化,先mark一下,不喜勿噴。

總結

知識都是由互通性的,多學習多思考,不能學了這個忘那個,要能融會貫通(互相抄襲借鑑)

由canvas實現btn效果有感

參考連結

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…

相關文章