出门忘带电源线,快递到了终于能够继续水文章了。好不容易得到一个面试机会,面试官很 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}`; } } }
效果图:
期间步骤只是思路过程,详情请查看项目源码,调试过程当中不可避免会进行一些修改。
面试前仍是要为面试刷下题目的,否则真的容易懵……