介紹
出門忘帶電源線,快遞到了終於可以繼續水文章了。好不容易獲得一個面試機會,面試官很 Nice,可惜的是當時處於懵逼狀態,錯過了大好的機會:
面試官:巴拉巴拉吧……
我:嗯,啊,這個,那(吱吱嗚嗚)……
面試官:你知道怎麼繪製三角形嘛?
我:主要是利用了 border
和 transparent
這兩個屬性。其餘邊設定為 transparent,然後將對應的方向設定為需要的顏色即可,一般常用等邊,等腰啊來裝飾一下。
面試官:那你知道不等邊三角形怎麼寫嗎?
我:不就是那麼寫麼(陷入懵逼狀態),然後又迅速說用偽元素來模擬一下?
面試官:你分別設定下高度不就好了。
我:……
效果展示:
三角形原理
通過圖形展示能夠更明顯顯示出區別:
1. 簡單的正方形
程式碼:
<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
複製程式碼
可以清晰的看到每個邊都是一個梯形。
2. 檢查正方形
開啟控制檯即可:
可以看到中間的空白即為我們設定的 100 * 100
,這是由於我們的盒模型(box-sizing)為 content-box
導致的結果。
那我們將其設定為 border-box
,檢視其結果:
由 border-box
可知,由於兩邊 border
大小為 60
,所以 60*2=120 > 100
,內部的 width 即為 0。
3. 預設盒模型的正方形
在上方已經說明了,正方形的 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
複製程式碼
4. 隱藏不必的邊
最後,生成三角形就水到渠成了(保留目標相反方向的顏色),舉幾個例子。
-
三角形開角向上:
$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 相關概念
0. 基本結構
<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
複製程式碼
效果圖:
1. 方向選擇
在開始寫一個三角形時,需要確定這個三角的朝向,如向上、向下、或向左上。這時候我們就需要一個點選的子元件來觸發效果了:
<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);
}
}
};
複製程式碼
效果圖:
2. 型別選擇
此處將三角形分為三種:等邊三角形、等腰三角形、不等邊三角形。
型別選擇元件依賴於方向元件,需要驗證傳入的值,並且在不同的值會有不同的輸出結果。在上文解釋過,斜方向的三角形是由兩個 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);
}
}
};
複製程式碼
效果圖:
3. 顏色選取
現在 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);
}
}
}
複製程式碼
效果圖:
4. 初步效果
效果圖來依賴於三個資料:方向、型別及顏色。依次適配這三個即可。
首先完成,方向及顏色問題,先初步看一下效果圖:
5. 寬高選取
在原理中說明了,三角形實際上是一個矩形隱藏了其餘 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;
}
},
}
};
複製程式碼
效果圖:
6. 生成程式碼
終於到了最後一步了,生成程式碼有很多方式,可以將之前從子元件傳遞出來的資料處理下輸出。這裡選擇一種較為取巧的形式,因為這邊使用的是行內 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}`;
}
}
}
複製程式碼
效果圖:
最後
期間步驟只是思路過程,詳情請檢視專案原始碼,除錯過程中不可避免會進行一些修改。
面試前還是要為面試刷下題目的,不然真的容易懵……
有沒有公司收前端切圖仔,求職中…