Material-UI-React-Layout-响应式布局

React+Electron桌面应用开发文章索引

继续前面的文章,这篇文章介绍基于MaterialUI的响应式布局方法。所谓响应式布局就是指在不同屏幕设备(如手机、平板、PC)上显示不同的布局格式,主要是根据屏幕宽度调整不同来匹配不同的布局,高度自动拉长或缩短。(一般主要是排列方式的改变,而内容并没有太大变化)

MaterialUI官方说明
MaterialDesign官方解说


断点BreakPoints

当页面宽度超过或低于某个像素数字的时候,我们就切换到新的布局,这个数字叫做断点BreakPoint.

MaterialUI默认的断点是:

  • xs, extra-small: 0px or larger
  • sm, small: 600px or larger
  • md, medium: 960px or larger
  • lg, large: 1280px or larger
  • xl, xlarge: 1920px or larger
响应式布局的断点

12栏布局

Material design默认把屏幕竖向分隔为12栏,使用柔性盒技术Flex box来实现。

来自网络的flexbox 布局教程

我们把网格单元Grid分为两类进行布局:Container容器和Item填充项目。

Item用百分比设定宽度,也就是它总是占父层的百分之多少,跟着父层一起放缩。Item之间有间隔,由父层Container的空隙spacing决定。

为了去除页面最外面的空隙,我们把body的边缘都去掉,打开index.html修改

<body style="margin: 0;padding: 0">

自动排列内容

下面是新的HomePage.js的代码,请注意render()里面:

  • 我们先创建12个items,每个item里面放一个卡片Card。
  • 最后创建一个Grid容器container,把12个item放进去。
  • 注意item设定xs=12,表示在极小屏幕(也就是宽度小于600像素)的时候,它占12栏充满父层;随着屏幕变大,超过600但小于960的时候,sm=6表示占父层的一半;以此类推。
import {
    Component
} from 'react'
import h from 'react-hyperscript'

import Button from 'material-ui/Button'
import Grid from 'material-ui/Grid'
import Card, {
    CardActions,
    CardContent
} from 'material-ui/Card';
import Typography from 'material-ui/Typography';

const styles = {
    container: {
        padding: 16,
    },
    item: {
        background: '#EEE'
    }
}

class Page extends Component {
    constructor(props) {
        super(props)
        this.state = {}
    }

    render() {
        let that = this

        let items = []
        for (let i = 0; i < 12; i++) {
            items.push(h(Grid, {
                item: true,
                xs: 12,
                sm: 6,
                md: 4,
                lg: 3,
                style: styles.item,
            }, [
                    h(Card, [
                        h(CardContent, 'Hello Card!')
                    ]),
                ]))
        }

        return h(Grid, {
            container: true,
            spacing: 16,
            style: styles.container,
        }, items)
    }
}

export default Page

得到效果如下图,手工拉宽浏览器观察不同效果(注意由于截图的原因,看起来卡片变小了,但实际并没有变小):


xs=12
sm=6
md=4
lg=3

自动宽度

我们希望添加左侧导航栏,它固定200像素宽度,右侧内容区自适应充满画面宽度。

为了让整个页面居中,我们修改index.html中的body:

<body style="margin: 0;padding: 0;text-align: center">

我们彻底修改一下HomePage的代码:

  • 使用withStyles标准的样式编写格式
  • Style的page设置了最大1000,inline-block让它居中
  • Style的left设置了width:200,
  • render()中的left和right都使用了两层Grid,外层item内层Container
  • return的div使用css.page样式,锁定最大宽度1000像素
  • div内还嵌套了一个Grid容器container,包含了left和right两个Grid(item)
import {
    Component
} from 'react'
import h from 'react-hyperscript'
import PropTypes from 'prop-types';
import {
    withStyles
} from 'material-ui/styles';

import Button from 'material-ui/Button'
import Grid from 'material-ui/Grid'
import Card, {
    CardActions,
    CardContent
} from 'material-ui/Card';
import Typography from 'material-ui/Typography';


const Style = (theme) => ({
    page: {
        maxWidth: 1000,
        width: '100%',
        display: 'inline-block'
    },
    left: {
        background: '#EEE',
        width: 200,
    },
    right: {
        background: '#CCC',
    },
    lfetBtn: {
        width: '100%',
        height: 56
    },
    rightBtn: {
        width: '100%',
        height: 56
    }
})

class Page extends Component {
    constructor(props) {
        super(props)
        this.state = {}
    }

    render() {
        let that = this
        const css = this.props.classes

        let rightItems = []
        for (let i = 0; i < 12; i++) {
            rightItems.push(h(Grid, {
                item: true,
                xs: 12,
                sm: 6,
                md: 4,
                lg: 3,
            }, [
                h(Button, {
                    className: css.rightBtn,
                }, 'hello!'),
            ]))
        }

        let leftItems = []
        for (let i = 0; i < 4; i++) {
            leftItems.push(h(Grid, {
                item: true,
                xs: 12,
            }, [
                h(Button, {
                    className: css.lfetBtn,
                }, 'Nav!'),
            ]))
        }

        let right = h(Grid, {
            item: true,
            xs: 12,
            sm: 8,
            className: css.right,
        }, h(Grid, {
            container: true,
        }, rightItems))


        let left = h(Grid, {
            item: true,
            className: css.left,
        }, h(Grid, {
            container: true,
        }, leftItems))

        return h('div', {
            className: css.page,
        }, [
            h(Grid, {
                container: true,
                justify: 'center',
            }, [
                left,
                right,
            ])
        ])
    }
}

Page.propTypes = {
    classes: PropTypes.object.isRequired,
}
export default withStyles(Style)(Page)

显示效果如下图,无论如何改变窗口宽度,左侧被锁定为200像素不变,右侧自动盛满。


自动尺寸

响应式显示和隐藏

对于上面的布局,当窗口超小xs的时候,left和right会变为竖向排列,看起来很糟糕。

我们可以让left在xs的时候自动隐藏,然后让一个同样功能的菜单自动出现。

首先我们需要导入Hidden元素。

import Hidden from 'material-ui/Hidden';

然后再把最后的left罩一层Hidden,设定only='xs',就是在极小窗口隐藏。
同样,我们创建一个top,它也是一个container嵌套item的grid结构,外层设定了justify: 'flex-end',alignItems: 'center'这是右对齐上下居中,我们设定了右侧的Button元素所在grid宽度100锁定,左侧div所在grid自动调整。

修改后的全部代码如下,重点注意底部render()方法:

import {
    Component
} from 'react'
import h from 'react-hyperscript'
import PropTypes from 'prop-types'
import {
    withStyles
} from 'material-ui/styles'

import Button from 'material-ui/Button'
import Grid from 'material-ui/Grid'
import Card, {
    CardActions,
    CardContent
} from 'material-ui/Card';
import Typography from 'material-ui/Typography';
import Hidden from 'material-ui/Hidden';

import Icon from 'material-ui/Icon';
import IconButton from 'material-ui/IconButton';
import MenuIcon from '@material-ui/icons/Menu';


const Style = (theme) => ({
    page: {
        maxWidth: 1000,
        width: '100%',
        display: 'inline-block'
    },
    left: {
        background: '#EEE',
        width: 200,
    },
    right: {
        background: '#CCC',
    },
    menuBtn: {
        width: '100%',
        height: 56
    },
    lfetBtn: {
        width: '100%',
        height: 56
    },
    rightBtn: {
        width: '100%',
        height: 56
    }
})

class Page extends Component {
    constructor(props) {
        super(props)
        this.state = {}
    }

    render() {
        let that = this
        const css = this.props.classes

        let rightItems = []
        for (let i = 0; i < 12; i++) {
            rightItems.push(h(Grid, {
                item: true,
                xs: 12,
                sm: 6,
                md: 4,
                lg: 3,
            }, [
                h(Button, {
                    className: css.rightBtn,
                }, 'hello!'),
            ]))
        }

        let leftItems = []
        for (let i = 0; i < 4; i++) {
            leftItems.push(h(Grid, {
                item: true,
                xs: 12,
            }, [
                h(Button, {
                    className: css.lfetBtn,
                }, 'Nav!'),
            ]))
        }

        let right = h(Grid, {
            item: true,
            xs: 12,
            sm: 8,
            className: css.right,
        }, h(Grid, {
            container: true,
        }, rightItems))


        let left = h(Grid, {
            item: true,
            className: css.left,
        }, h(Grid, {
            container: true,
        }, leftItems))

        let top = h(Grid, {
            container: true,
            justify: 'flex-end',
            alignItems: 'center'
        }, [
            h(Grid, {
                item: true,
                xs: 8,
            }, h('div', '')),
            h(Grid, {
                item: true,
                style: {
                    width: 100
                }
            }, h(Button, {
                className: css.menuBtn,
            }, h(MenuIcon)))
        ])

        return h('div', {
            className: css.page,
        }, [
            h(Grid, {
                container: true,
                justify: 'center',
            }, [
                h(Hidden, {
                    only: ['sm', 'md', 'lg', 'xl']
                }, top),
                h(Hidden, {
                    only: ['xs']
                }, left),

                right,
            ])
        ])
    }
}

Page.propTypes = {
    classes: PropTypes.object.isRequired,
}
export default withStyles(Style)(Page)

实际效果是当窗口宽度达到xs的时候,顶部按钮就会自动显示出来。


自动显示和隐藏

此外,还有一些其他解决方案实现隐藏和显示,参考官方的Grid案例

菜单按钮可以点击,但还不能弹出内容,可以参照这里的官方文档创建


致力于让一切变得简单

如果您发现文章错误,请不吝留言指正;
如果您觉得有用,请点喜欢;
如果您觉得很有用,欢迎转载~


END

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,725评论 1 92
  • Grid 是CSS中最强大的布局系统。它是2-Dimensional System,这意味着它可以同时处理列和行....
    邢烽朔阅读 2,577评论 0 5
  • 1. 前言 前端圈有个“梗”:在面试时,问个css的position属性能刷掉一半人,其中不乏工作四五年的同学。在...
    YjWorld阅读 4,422评论 5 15
  • 原文连接 一.如何构建自己的栅格系统 grid design how your grid behaves at d...
    JamesSawyer阅读 2,449评论 0 2
  • 谨以此文献给亲爱的老师、同学们,还有学校的朝露夕晖一草一木。今年是毕业25周年,秋高气爽的日子,期待我们一起回到那...
    步绾阅读 409评论 6 6