CSS 搞事技巧:border+transparent

介紹

出門忘帶電源線,快遞到了終於能夠繼續水文章了。好不容易得到一個面試機會,面試官很 Nice,惋惜的是當時處於懵逼狀態,錯過了大好的機會:css

面試官:巴拉巴拉吧……html

我:嗯,啊,這個,那(吱吱嗚嗚)……前端

面試官:你知道怎麼繪製三角形嘛?vue

我:主要是利用了 bordertransparent 這兩個屬性。其他邊設置爲 transparent,而後將對應的方向設置爲須要的顏色便可,通常經常使用等邊,等腰啊來裝飾一下。git

面試官:那你知道不等邊三角形怎麼寫嗎?github

我:不就是那麼寫麼(陷入懵逼狀態),而後又迅速說用僞元素來模擬一下?面試

面試官:你分別設置下高度不就行了。app

我:……dom

效果展現:ide

示例源碼在線示例

三角形原理

經過圖形展現可以更明顯顯示出區別:

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. 隱藏沒必要的邊

最後,生成三角形就水到渠成了(保留目標相反方向的顏色),舉幾個例子。

  1. 三角形開角向上:

    $square-size = 0
    $border-size = 60px
    
    .triangle
      width $square-size
      height $square-size
      border $border-size solid transparent
      border-bottom-color #A72310
    複製代碼

  1. 三角形開角向右:

    $square-size = 0
    $border-size = 60px
    
    .triangle
      width $square-size
      height $square-size
      border $border-size solid transparent
      border-left-color #0C1F22
    複製代碼

  1. 三角形開角向左上:

    $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}`;
    }
  }
}
複製代碼

效果圖:

最後

期間步驟只是思路過程,詳情請查看項目源碼,調試過程當中不可避免會進行一些修改。

面試前仍是要爲面試刷下題目的,否則真的容易懵……

有沒有公司收前端切圖仔,求職中…

相關文章
相關標籤/搜索