栅栏布局合并html,vue 实现(抄袭)一个简单的栅格组件

vue 实现(抄袭)一个简单的栅格组件

参考iview, ant-design 的栅格组件,发现两者的基础思路是一致的。

这里通过实现一个简化版的栅格组件做总结.

目标

实现24格栅格布局,包括组件 Row(行), Col(列)

组件可嵌套, 容器大小自适应

响应式布局

基本原理

栅格组件,可以看成对横向宽度的分割。

例如:实现左右平分布局

// html

// css

.contianer{

display: flex;

justify-content: space-between;

}

.left,

.right{

flex: 0 0 auto;

width: 50%;

height: 100px;

}

.left{

background: blue;

}

.right{

background: orange;

}

实现栅格组件的基本思路与二等分基本一致,既是根据 Col组件不同的 span 值,动态的切换对应的等分样式, 所以重点在css配置及如何切换

实现

这里基础布局会借用flex, css使用less编写,组件样式使用前缀做区分。

Row 基本结构

// 内容插槽

import { Vue, Component, Prop, Provide } from 'vue-property-decorator'

const prefix = 'b-row'

@Component

export default class Row extends Vue {

// 组件类名

get classList (){

return [

prefix,

]

}

}

Col 基础结构

import { Component, Vue, Prop, Inject, } from "vue-property-decorator"

const prefix = "b-col"

@Component

export default class Col extends Vue {

// 接口定义

@Prop() span?:number

@Prop() xs?:number

@Prop() sm?:number

@Prop() md?:number

get classList() {

return {

[prefix]: true

}

}

}

布局样式

这里我们希望通过设置 Col 的 span 属性分配列组件所占比例,

占满一行 span=24.

一种实现是通过js计算单个组件所占的比例,设置到容器行内样式上。

一种是将每一种分割类型的样式,都写入css, 通过类名的方式做区分

iview ant-design 使用的后者, 猜想原因: 一方面是对性能的考虑,另一方面从实际使用上,24切分基本满足大多数情况.

@prefix-row: ~'b-row';

@prefix-col: ~'b-col';

@grid-columns: 24;

// Row 基础样式

.@{prefix-row}{

position: relative;

display: flex;

justify-content: flex-start;

align-items: flex-start;

flex-wrap: wrap;

height: auto;

}

// Col 基础样式

.@{prefix-col}{

overflow: hidden;

flex: 0 0 auto;

box-sizing: border-box;

}

// 创建切分样式

// 因为每一切分的样式逻辑相同, 我们可以使用函数实现

.loop-grid-column(@index, @class) when(@index > 0) {

// 拼接类名 例如:b-col-span-1, 这里的 @class 主要为响应式设置不同的类名时使用

.@{prefix-col}-@{calss}-@{index}{

display: block;

box-sizing: border-box;

width: @index / @grid-columns * 100%;

}

// 递归调用

.loop-grid-column((@index - 1), @class)

}

// @index = 0 是的样式

.loop-grid-column(@index, @class) when(@index = 0) {

.@{prefix-col}-@{sizeType}-@{index}{

display: none;

}

}

/*

编译后:

.b-col-span-1{ ... }

.b-col-span-2{ ... }

.b-col-span-3{ ... }

.b-col-span-4{ ... }

.b-col-span-5{ ... }

.b-col-span-6{ ... }

.b-col-span-7{ ... }

....

*/

.make-grid(@class){

.loop-grid-column(@grid-columns, @class);

}

// span 切分

.make-grid(~'span')

// 响应式

@media (max-width: 575px) {

.makeGrid(~'xs')

}

@media (min-width: 576px) {

.makeGrid(~'sm')

}

@media (min-width: 768px) {

.makeGrid(~'md')

}

@media (min-width: 992px) {

.makeGrid(~'lg')

}

// 这里需要注意样式的先后顺序, 保证大尺寸样式覆盖小尺寸

设置动态类名

现在我们需要根据 span 或 响应配置 动态的设置 Col 的类名,以对应到不同的css样式上.

// col-html

// col-ts

get classList(){

let vm:any = this;

let { span, xs, sm, md } = this;

// 切分接口类型

let sizeType:string[] = ['xs', 'sm', 'md', 'span']

let typeClass = { [prefix]: true};

sizeType.forEach((type: string) =>{

let size = (vm[type]);

let temp = {}

// 如果切分类型存在值, 拼接对应的类名

if(typeof size === 'number'){

temp = {

[`${prefix}-${type}-${size}`]: true

}

}

// 合并类名

typeClass = Object.assign(typeClass, temp)

})

return typeClass

}

/*

例如:

{

b-col-span-10: true,

b-col-xs-24: true,

b-col-sm-12: true

}

*/

完整例子

Row.vue

import { Vue, Component, Prop, Provide } from 'vue-property-decorator'

const prefix = 'b-row'

@Component

export default class Row extends Vue {

@Provide() rowContext = this

@Prop({default: 0}) gutter!:number

private time:Date = new Date()

// computed

get classList (){

return [

prefix,

]

}

get style (){

return this.gutter && this.gutter > 0 ? {

'margin-left': `-${this.gutter/2}px`,

'margin-right': `-${this.gutter/2}px`

} : {}

}

}

Col.vue

import { Component, Vue, Prop, Inject, } from "vue-property-decorator"

const prefix = "b-col"

@Component

export default class Col extends Vue {

@Inject() rowContext?:any

@Prop() span?:number

@Prop() xs?:number

@Prop() sm?:number

@Prop() md?:number

@Prop() order?:number

get classList(){

let vm:any = this;

let { span, xs, sm, md } = this;

let sizeType:string[] = ['xs', 'sm', 'md', 'span']

let typeClass = { [prefix]: true};

sizeType.forEach((type: string) =>{

let size = (vm[type]);

let temp = {}

if(typeof size === 'number'){

temp = {

[`${prefix}-${type}-${size}`]: true

}

}

typeClass = Object.assign(typeClass, temp)

})

return typeClass

}

get style(){

let order = this.order;

let gutter = this.rowContext.gutter;

let baseStyle = {

'order': `${order !== undefined ? order : 0 }`

};

let pdStyle = {};

if(gutter > 0){

pdStyle = {

'padding-left': `${gutter/2}px`,

'padding-right': `${gutter/2}px`,

}

}

return Object.assign({}, baseStyle, pdStyle);

}

}

index.less

@prefix-row: ~'b-row';

@prefix-col: ~'b-col';

@gird-columns: 24;

.loop-grid-columns(@index, @sizeType) when (@index > 0){

.@{prefix-col}-@{sizeType}-@{index}{

display: block;

box-sizing: border-box;

width: @index / @gird-columns * 100%;

}

.loop-grid-columns((@index - 1), @sizeType)

}

.loop-grid-columns(@index, @sizeType) when (@index = 0){

.@{prefix-col}-@{sizeType}-@{index}{

display: none;

}

}

.clearfix{

&::after{

content: '';

display: table;

}

clear: both;

}

.mackRow(@gutter:0){

.clearfix;

position: relative;

display: flex;

justify-content: flex-start;

align-items: flex-start;

flex-wrap: wrap;

margin-left: (@gutter/-2)px;

margin-right: (@gutter/-2)px;

height: auto;

}

.@{prefix-col}{

overflow: hidden;

flex: 0 0 auto;

box-sizing: border-box;

}

// flex 水平布局

@justify: center, flex-start, flex-end, space-between, space-around;

.loop-flex-justify(@i: 1) when (@i < length(@justify) + 1) {

@type: extract(@justify, @i);

@prop: ~'[justify="@{type}"]';

&@{prop}{

justify-content: @type;

}

.loop-flex-justify(@i + 1)

}

// flex 垂直布局

@align: flex-start, flex-end, center, baseline, stretch;

.loop-flex-align(@i: 1) when (@i < length(@align) + 1){

@type: extract(@align, @i);

@prop: ~'[align="@{type}"]';

&@{prop}{

align-items: @type;

}

.loop-flex-align((@i + 1))

}

.@{prefix-row}{

.mackRow();

box-sizing: border-box;

background: #eee;

.loop-flex-justify();

.loop-flex-align();

}

.makeGrid(@class){

.loop-grid-columns(@gird-columns, @class);

}

.makeGrid(~'span');

@media (max-width: 575px) {

.makeGrid(~'xs')

}

@media (min-width: 576px) {

.makeGrid(~'sm')

}

@media (min-width: 768px) {

.makeGrid(~'md')

}

@media (min-width: 992px) {

.makeGrid(~'lg')

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值