出門忘帶電源線,快遞到了終於能夠繼續水文章了。好不容易得到一個面試機會,面試官很 Nice,惋惜的是當時處於懵逼狀態,錯過了大好的機會:css
面試官:巴拉巴拉吧……html
我:嗯,啊,這個,那(吱吱嗚嗚)……vue
面試官:你知道怎麼繪製三角形嘛?git
我:主要是利用了 border
和 transparent
這兩個屬性。其他邊設置爲 transparent,而後將對應的方向設置爲須要的顏色便可,通常經常使用等邊,等腰啊來裝飾一下。github
面試官:那你知道不等邊三角形怎麼寫嗎?面試
我:不就是那麼寫麼(陷入懵逼狀態),而後又迅速說用僞元素來模擬一下?app
面試官:你分別設置下高度不就行了。dom
我:……ide
效果展現:post
經過圖形展現可以更明顯顯示出區別:
代碼:
<div class="square"></div>
$square-size = 100px .square width $square-size height $square-size border 5px solid border-color #893615 #E76B56 #A72310 #0C1F22
效果圖:
增強一下效果:
$square-size = 100px $border-size = 60px .square width $square-size height $square-size border $border-size solid border-color #893615 #E76B56 #A72310 #0C1F22
能夠清晰的看到每一個邊都是一個梯形。
打開控制檯便可:
能夠看到中間的空白即爲咱們設置的 100 * 100
,這是因爲咱們的盒模型(box-sizing)爲 content-box
致使的結果。
那咱們將其設置爲 border-box
,查看其結果:
由 border-box
可知,因爲兩邊 border
大小爲 60
,因此 60*2=120 > 100
,內部的 width 即爲 0。
在上方已經說明了,正方形的 size 被擠壓爲 0 時就會獲得三角形的效果。
那麼此處就在默認盒模型的狀況下建立一個三角形:
$square-size = 0 $border-size = 60px .square width $square-size height $square-size border $border-size solid border-color #893615 #E76B56 #A72310 #0C1F22
最後,生成三角形就水到渠成了(保留目標相反方向的顏色),舉幾個例子。
三角形開角向上:
$square-size = 0 $border-size = 60px .triangle width $square-size height $square-size border $border-size solid transparent border-bottom-color #A72310
三角形開角向右:
$square-size = 0 $border-size = 60px .triangle width $square-size height $square-size border $border-size solid transparent border-left-color #0C1F22
三角形開角向左上:
$square-size = 0 $border-size = 60px .triangle width $square-size height $square-size border $border-size solid transparent border-left-color #0C1F22 border-top-color #893615
每次還要想想怎麼寫三角形很麻煩,將其可視化,每次只須要點一點就建立一個三角形纔是極好的。
友情提示:如下涉及 Vue 相關概念
<Layout-Layout :background-color="bgColor" class="generate-triangle" > <aside class="settings"> <section class="settings_direction"> <h4 class="title">三角形方向</h4> </section> <section class="settings_type"> <h4 class="title">三角形類型</h4> </section> <section class="settings_color"> <h4 class="title">三角形顏色</h4> </section> </aside> <main class="exhibition"> <section class="rendering"> <h4>效果圖</h4> </section> <section class="code"> <h4>代碼</h4> </section> </main> </Layout-Layout>
.generate-triangle display flex .title margin 0 padding 0 .settings flex-basis 30% .exhibition flex auto background-color #cdd1d3 // 銀魚白 .settings display flex flex-direction column padding-top 12px .settings_direction, .settings_type, .settings_color display flex justify-content center .settings_type, .settings_color flex-basis 20% .settings_direction flex auto .exhibition display flex flex-direction column padding-top 12px .rendering, .code display flex justify-content center .code flex-basis 35% .rendering flex auto
效果圖:
在開始寫一個三角形時,須要肯定這個三角的朝向,如向上、向下、或向左上。這時候咱們就須要一個點擊的子組件來觸發效果了:
<div class="triangle-direction"> <section :class="direction.name === 'oblique' ? 'square-t45' : 'square'" v-for="(direction, index) in directions" :key="index" > <div class="single" v-for="(item, index) in direction.single" :key="index" :class="{active: direction.name + index === active}" @click.stop="changeDirection(item, direction.name + index)" > </div> </section> </div>
export default { name: "triangle-direction", data: () => { return { active: "oblique0", directions: [ { name: "oblique", single: ["top", "right", "bottom", "left"] }, { name: "positive", single: ["top-left", "top-right", "bottom-right", "bottom-left"] } ] }; }, mounted() { this.changeDirection("top", "oblique0"); }, methods: { changeDirection(val, index) { this.active = index; this.$emit("getDirection", val); } } };
效果圖:
此處將三角形分爲三種:等邊三角形、等腰三角形、不等邊三角形。
類型選擇組件依賴於方向組件,須要驗證傳入的值,而且在不一樣的值會有不一樣的輸出結果。在上文解釋過,斜方向的三角形是由兩個 border
組成,因此這種類型的將不提供等邊的形式:
<div class="triangle-type"> <button class="type-button" v-for="(type, index) in triangleTypes" v-show="type.en !== 'equilateral' || equilateral" :key="index" :class="{active: index === active}" @click.stop="changeType(type.en, index)" >{{type.zh}}</button> </div>
export default { name: "triangle-type", data: () => { return { active: 0, equilateral: false, triangleTypes: [ { en: "equilateral", zh: "等邊" }, { en: "isosceles", zh: "等腰" }, { en: "scalene", zh: "不等邊" } ] }; }, props: { type: { type: String, validator: function(val) { return [ "top", "right", "left", "bottom", "top-left", "top-right", "bottom-left", "bottom-right" ].includes(val); } } }, watch: { type: { handler: function(val) { const isPositive = ["top", "right", "left", "bottom"].includes(val); this.equilateral = isPositive; if (isPositive) { this.changeType('equilateral', 0); } else { this.changeType('isosceles', 1); } }, immediate: true } }, methods: { changeType(item, index) { this.active = index; this.$emit("getType", item); } } };
效果圖:
如今 input 提供了 type="color"
這一選項,製做一個顏色選擇器仍是很簡單的,對於 input 可使用以前說起的 CSS 搞事技巧:checkbox+label+selector 來隱藏它:
<div class="color-picker"> <label for="color-picker"> <span class="color-name" :style="{backgroundColor: color}"> {{color}} </span> <input type="color" v-model="color" id="color-picker" @change="changeColor"> </label> </div>
export default { name: 'color-picker', data: () => { return { color: '#000000' } }, mounted() { this.changeColor(); }, methods: { changeColor() { this.$emit('getColor', this.color); } } }
效果圖:
效果圖來依賴於三個數據:方向、類型及顏色。依次適配這三個便可。
首先完成,方向及顏色問題,先初步看一下效果圖:
在原理中說明了,三角形其實是一個矩形隱藏了其他 border 造成的。以方向等邊三角形爲例子:若須要邊長度爲 50px
的的三角形,則根據勾股定理可得出:border-width: 0 28.87px 50px;
<div class="triangle-width"> <div class="width-inputs"> <input v-model="bottom" class="width-input" type="number" min="0" max="180" placeholder="底" :disabled="!isPositive" @change="getBorder" > <input v-model="sideOne" class="width-input" type="number" min="0" max="180" placeholder="邊" :disabled="type !== 'isosceles' && type !== 'scalene'" @change="getBorder" > <input v-model="sideTwo" class="width-input" type="number" min="0" max="180" placeholder="側邊" :disabled="type !== 'scalene'" @change="getBorder" > </div> </div>
export default { name: "triangle-width", props: { type: { type: String, validator: function(val) { return ["equilateral", "isosceles", "scalene"].includes(val); } }, direction: { type: String, validator: function(val) { return [ "top", "right", "left", "bottom", "top-left", "top-right", "bottom-left", "bottom-right" ].includes(val); } } }, data: () => { return { bottom: 50, sideOne: 50, sideTwo: 50, borderWidth: '', isPositive: false }; }, watch: { direction: { handler: function(val) { this.isPositive = ["top", "right", "left", "bottom"].includes(val) this.getBorder(); }, immediate: true }, type: { handler: function() { this.getBorder(); } } }, methods: { getBorder() { let direction = this.direction; let type = this.type; switch(type) { case 'equilateral': this.calcEquBorder(direction); break; case 'isosceles': this.calcIsoBorder(direction); break; case 'scalene': this.calcScaBorder(direction); break; default: break; } this.$emit('getBorderWidth', this.borderWidth); }, calcEquBorder(direction) { let bottom = this.bottom; let height = (bottom / Math.sqrt(3)).toFixed(2); switch(direction) { case 'top': this.borderWidth = `0 ${height}px ${bottom}px`; break; case 'right': this.borderWidth = `${height}px 0 ${height}px ${bottom}px`; break; case 'bottom': this.borderWidth = `${bottom}px ${height}px 0`; break; case 'left': this.borderWidth = `${height}px ${bottom}px ${height}px 0`; break; default: break; } }, } };
效果圖:
終於到了最後一步了,生成代碼有不少方式,能夠將以前從子組件傳遞出來的數據處理下輸出。這裏選擇一種較爲取巧的形式,由於這邊使用的是行內 style 樣式,因此能夠直接在它的 DOM 上獲取。
<div class="triangle" ref="triangleRendering" :style="[borderStyle, { borderWidth: borderWidth }]"></div>
export default { methods: { postCode() { this.$nextTick(() => { let dom = this.$refs.triangleRendering; let code = dom.attributes.style.textContent; this.$emit('getCode', code); }) } } }
export default { name: 'triangle-code', props: { code: { type: String, required: true } }, watch: { code: { handler: function(code) { this.handleCode(code); }, immediate: true } }, data: () => { return { copyCode: '' } }, methods: { handleCode(code) { code = code.replace(/\;/g,";\n"); this.copyCode = `width: 0;\n height: 0;\n border: solid transparent;\n ${code}`; } } }
效果圖:
期間步驟只是思路過程,詳情請查看項目源碼,調試過程當中不可避免會進行一些修改。
面試前仍是要爲面試刷下題目的,否則真的容易懵……