week7---10月23日 DIV布局定位(一)

本文详细介绍了CSS中的五种布局定位属性:自动定位、相对定位、绝对定位、固定定位和绝对定位元素的裁剪,并对每种定位方式的特点进行了说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、复习

1、div边框的设置属性

2、属性不同个数属性值的赋值方式


二、新课

(一)布局定位属性position

布局就是将元素放置在页面的指定位置,联合使用定位、浮动可创建按列布局、重叠、表格等多种布局效果。

    CSS有三种布局机制:

  • 普通文档流布局(默认)
  • 定位布局
  • 浮动布局

    普通文档流就是由浏览器自动定位,默认从上到下依次排列html文档中的元素。

  • <div>、<p>等盒状块级框元素在普通流中只能整体设置样式,如边框、边距等,不能改变它在页面中的位置。
  •  单独的一行文本称为“行框”,只能设置行高度。
  •  <span>、<strong>等行内元素称为“行内框”,只能设置在行中的水平内外边距,不能设置垂直的内外边距—设置无效。 
1、元素的定位样式

CSS可以对任何元素进行定位,可以按浏览器窗口或父元素的坐标定位,也可以相对自己原来的位置定位。 

样式属性

取值和描述

position:定位方式;

应配合left、right、top、bottom使用

static(默认)自动定位

relative 相对定位

absolute绝对定位

fixed      固定定位

left:      左侧偏移量或坐标;

必须配合position使用

right:   右侧偏移量或坐标;

必须配合position使用

top:      顶端偏移量或坐标;      

必须配合position使用

bottom:下端偏移量或坐标;

必须配合position使用

clip:裁剪形状;

       —仅用于绝对定位元素

auto不裁剪(默认)

rect(top, right, bottom, left) 指定矩形

1)自动定位 static

    自动定位(默认方式):position:static;

    自动定位就是元素在页面普通文档流中由html自动定位,普通文档流中的元素也称为流动元素。

    自动定位的块级元素若不设置大小,则宽度默认为浏览器页面宽度、高度自适应所包含内容的高度。

    自动定位时top、bottom、left、right设置无效。


2)绝对定位 absolute

绝对定位:position:absolute;

        绝对定位是将元素依据已经定位(绝对、固定或相对定位)的父元素进行定位,父元素没有定位或没有父元素则默认依据body浏览器窗口定位。

        绝对定位的元素不论本身是什么类型,定位后都将成为一个新的块级盒框,如果未指定宽度高度默认自适应实际包含的内容区域(不再默认浏览器宽度)。定位后将重叠覆盖新位置的原有元素,它原来在正常文档流中所占的空间同时被关闭,就是说绝对定位的元素不占据页面空间,原空间被后继元素使用。

        绝对定位必须用left、right、top、bottom属性之一激活,用于指定元素左、右、上、下外边距距离已定位父元素(或浏览器)左、右、上、下内边框的距离,或者说用已定位父元素对应边框向中心的偏移量作为定位元素对应边的位置。

       如果定义多个属性,当left、right冲突时以left为准,当top、bottom冲突时以top为准,如果一个也不指定则元素仍按普通文档流布局,但它自己不再占据空间,后续元素上移与其重叠。

绝    对定位元素定位后相对父元素的位置不再变化,若页面内容较多拖动页面滚动时,定位元素会随页面一起滚动。

        绝对定位元素重叠覆盖其他元素时可用z-index属性设置它们的叠放次序。

        注意:若直接父元素不定位时,子元素将依据再上级已定位的某个父元素(或浏览器)绝对定位,页面调整时定位子元素相对直接父元素的位置将会发生变化。因此如果直接父元素不需要定位,而子元素必须根据直接父元素绝对定位时,可将父元素设置为相对定位但不设偏移量(不失去空间也不影响位置)即可保证子元素依据直接父元素准确定位。 


3)绝对定位元素的裁剪clip

clip:裁剪形状;

        clip属性仅对绝对定位的元素设置被裁剪时的形状,裁剪掉不需要的部分或指定显示区域,当元素(如图像)实际尺寸大于clip指定区域时只显示clip区域的内容,等同于指定区域并设置overflow:hidden的效果。

        裁剪形状属性值:

        auto    不裁剪(默认)由浏览器设置元素的形状  

        rect(top, right, bottom, left)指定矩形区域为裁剪后保留区域

        inherit  指定从父元素继承clip属性—直到IE8目前都不支持

        clip属性目前只能指定按矩形区域裁剪,top, right, bottom, left分别为显示区域按顺时针对应四边距离被裁剪元素左上角(0,0)的距离,或者理解为元素显示的矩形区域左上角(left, top)及右下角(right, bottom)坐标。

        top, right, bottom, left可以是任意带单位的数值、也可以是被裁剪元素对应边长的百分比%,取值0或auto表示该边不裁剪。如果指定的剪裁区域大于元素实际区域则不裁剪。


4)固定定位fixed

固定定位:position:fixed;

    固定定位与绝对定位absolute相似,定位后元素也生成为新块级盒框、覆盖新位置原有元素,在正常文档流中所占的原空间关闭—被后继元素使用。

    固定定位与绝对定位absolute的区别是定位的元素无论父元素是否定位都会直接在浏览器窗口中定位—不会随滚动条拖动页面而滚动,固定定位用left、right、top、bottom指定浏览器左、右、上、下各边向中心的偏移量作为定位元素外边距位置

    例如在浏览器四个角各放置一个width:40px; height:40px;的矩形盒框:

position:fixed;top:50px; left:50px;           左上角定位

position:fixed;top:50px; right:50px;        右上角定位

position:fixed;bottom:50px; right:50px; 右下角定位

position:fixed;bottom:50px; left:50px;     左下角定位

注意:IE6及以下版本不支持position:fixed固定定位。


5)相对定位relative

相对定位:position:relative;

    相对定位是让元素(可以是行内元素)相对于它在正常文档流原位置按left、right、top和bottom偏移量移动到新位置。

    相对定位的元素移动后保持原外观样式大小、移动定位后不会占据新空间会覆盖新位置原有元素,原位置空间被保留,其他元素相对它原来的位置不变。

    left、right、top和bottom指定相对原位置移动的偏移量,可以使用带单位数值、相对父元素的百分比%

    left       正值:左边向内—向右移动,负值:左边向外—向左

    right    正值:右边向内—向左移动,负值:右边向外—向右

    top       正值:上边向内—向下移动,负值:上边向外—向上

    bottom正值:下边向内—向上移动,负值:下边向外—向下   

    例如:left:20px;  元素左边框右移20像素

                left:-20px; 元素左边框左移20像素。

    例如:position:relative; left:350px; bottom:150px;

    相对原始位置左边右移350、下边上移150像素—原空间保留。



<template> <view class="calendar-container" style="margin: 20rpx 0; margin-bottom: 0"> <!-- 头部显示 --> <!-- <view class="header"> <text class="year-month">{{ displayYearMonth }}</text> --> <!-- <img class="person-img" style="width: 120rpx" :src="IMG_URL + 'schedulePerson.png'" /> --> <!-- <img width="100%;" style="border-top-left-radius: 32rpx; border-top-right-radius: 32rpx" :src="IMG_URL + 'date-header.png'" /> --> <view class="fr jb ac" style="margin: 0 40rpx"> <view class="fr jb ac"> <!-- 向左箭头上--> <image class="arrow-left" v-if="isShowArrowLeft" style="width: 30rpx; height: 30rpx; margin-right: 10rpx; transform: rotate(180deg)" :src="IMG_URL + 'before_icon.png'" @click="handleArrowLeft" /> <image class="arrow-left" v-if="!isShowArrowLeft" style="width: 30rpx; height: 30rpx; margin-right: 10rpx" :src="IMG_URL + 'after_icon.png'" @click="handleArrowLeft" /> <!-- 中间显示--> <view class="date-change-btn">{{ formattedDate }}</view> <!-- 向右箭头下--> <image class="arrow-right" v-if="isShowArrowRight" style="width: 30rpx; height: 30rpx; margin-left: 10rpx" :src="IMG_URL + 'before_icon.png'" @click="handleArrowRight" /> <image class="arrow-right" v-if="!isShowArrowRight" style="width: 30rpx; height: 30rpx; margin-left: 10rpx; transform: rotate(180deg)" :src="IMG_URL + 'after_icon.png'" @click="handleArrowRight" /> </view> <!-- <view class="fr jb ac" v-if="props.isShow != false"> <view class="date-change-btn"> <image style="width: 40rpx; height: 40rpx; margin-right: 20rpx" :src="IMG_URL + 'filtration.png'" /> </view> </view> --> </view> <!-- 滑动容器 --> <!-- <swiper :current-item-id="currentItemId" @change="onSwiperChange" class="swiper-container" :duration="300"> --> <swiper :current-item-id="currentItemId" @change="throttledSwiperChange" class="swiper-container" :duration="300" :circular="false"> <swiper-item v-for="item in weekArray" :key="item" :item-id="item.toString()" class="swiper-item"> <view class="week-container"> <view v-for="day in weekDateObject.obj[item]" :key="day.date" class="day-item" @tap="selectDay(day)"> <text class="weekday">{{ day.weekday }}</text> <text class="day-number" :class="{ today: day.isToday, current: currentSelected === formatDate1(day.date) }">{{ day.day }}</text> <!-- <view v-if="dataArray?.includes(formatDate1(day.date))" style="background-color: #ffbd27; width: 6rpx; height: 6rpx; border-radius: 999rpx; margin-top: 4rpx"> </view> --> <image v-if="dataArray.includes(formatDate1(day.date))" style="width: 10rpx; height: 10rpx; margin-top: 4rpx" :src="IMG_URL + 'star_a.png'" /> </view> </view> </swiper-item> </swiper> </view> </template> <script setup> import { IMG_URL } from '@/config/base' import { getCalendarStatistics } from '@/request/api/org/course.js' import { addWeeks, eachDayOfInterval, endOfWeek, format, isSameDay, isToday, startOfWeek, subWeeks } from 'date-fns' import { zhCN } from 'date-fns/locale' import { throttle } from 'lodash-es' import { computed, ref, watch } from 'vue' const emit = defineEmits(['date-change']) // 当前基准期(中间位置) const baseDate = ref(new Date()) const currentItemId = ref('0') const isInitialized = ref(false) // 添加初始化标志 const props = defineProps({ courseId: { type: String, default: '' }, // 兼容c端约课组件 type: { type: Number, default: 1 }, function: { type: Function, default: null }, // 兼容c端约课组件 courseMode: { type: Number, default: null }, //插班补课是否显示筛选 isShow: { type: Boolean, default: true } }) // 预渲染的周数据(前周/当前周/下周) const formatDate1 = dateString => { const date = new Date(dateString) const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') return `${year}-${month}-${day}` } const userInfo = uni.getStorageSync('USERINFO') const orgId = computed(() => uni.getStorageSync('orgInfo').orgId || {}) // const weekArray = ref([-4, -3, -2, -1, 0, 1, 2, 3, 4]) const weekArray = ref([-1, 0, 1]) // 只保留前周/当前周/下周 const dataArray = ref([]) const weekDateObject = computed(() => { const finallyArray = weekArray.value.map(week => { let curDate if (week < 0) { curDate = subWeeks(baseDate.value, Math.abs(week)) } else if (week == 0) { curDate = baseDate.value } else { curDate = addWeeks(baseDate.value, week) } const start = startOfWeek(curDate, { weekStartsOn: 1 }) const end = endOfWeek(curDate, { weekStartsOn: 1 }) return { [week]: eachDayOfInterval({ start, end }).map(day => ({ date: day, day: format(day, 'd'), // 使用自定义函数截取第二个字符(去掉"周"字) weekday: format(day, 'EEE', { locale: zhCN }).substring(1), isToday: isToday(day), isCurrent: isSameDay(day, new Date()) })) } }) console.log('finallyArray', finallyArray) const obj = {} finallyArray.forEach(item => { obj[Object.keys(item)[0]] = Object.values(item)[0] }) const returnObj = { obj, startTime: formatDate1(obj[weekArray.value[0]][0].date), endTime: formatDate1(obj[weekArray.value[weekArray.value.length - 1]][6].date) } console.log(obj, 'obj') return returnObj }) const getScheduleData = () => { if (!weekDateObject.value) { return } const params = { beginTime: `${weekDateObject.value.startTime} 00:00:00`, endTime: `${weekDateObject.value.endTime} 23:59:59`, orgId: orgId.value, userId: userInfo.userId } if (props.courseId) { params.courseId = props.courseId } // 兼容c端约课组件 if (props.courseMode) { params.courseMode = props.courseMode } let port = getCalendarStatistics // 兼容c端约课组件 if (props.type == 1) { port = getCalendarStatistics } else { port = props.function } console.log(port, 'port') port(params).then(res => { dataArray.value = res.data?.map(v => v.scheduleDate) // console.log(dataArray.value, "dataArray"); }) } watch( () => [weekDateObject.value, props.courseId], () => { getScheduleData() }, { immediate: true, deep: true } ) // 判断上周是否有课表 const isShowArrowLeft = computed(() => { const prevWeekMonday = format(startOfWeek(subWeeks(baseDate.value, 1), { weekStartsOn: 1 }), 'yyyy-MM-dd') return dataArray.value.includes(prevWeekMonday) }) // 判断下周是否有课表 const isShowArrowRight = computed(() => { const nextWeekMonday = format(startOfWeek(addWeeks(baseDate.value, 1), { weekStartsOn: 1 }), 'yyyy-MM-dd') return dataArray.value.includes(nextWeekMonday) }) const handleArrowLeft = () => { const { start, end } = getLastWeek() baseDate.value = subWeeks(baseDate.value, 1) emit('date-search', start, end) } const handleArrowRight = () => { const { start, end } = getNextWeek() baseDate.value = addWeeks(baseDate.value, 1) emit('date-search', start, end) } // 新增辅助方法获取期范围 const getDateRange = date => { const start = format(startOfWeek(date, { weekStartsOn: 1 }), 'yyyy-MM-dd') const end = format(endOfWeek(date, { weekStartsOn: 1 }), 'yyyy-MM-dd') return [start, end] } // 重构 onSwiperChange 方法 // const onSwiperChange = e => { // const curId = Number(e.detail.currentItemId) // currentItemId.value = curId.toString() // // 向左滑动加载前周 // if (curId === weekArray.value[0]) { // baseDate.value = subWeeks(baseDate.value, 1) // emit('date-search', ...getDateRange(baseDate.value)) // } // // 向右滑动加载下周 // if (curId === weekArray.value[weekArray.value.length - 1]) { // baseDate.value = addWeeks(baseDate.value, 1) // emit('date-search', ...getDateRange(baseDate.value)) // } // } const onSwiperChange = e => { const curId = Number(e.detail.currentItemId) currentItemId.value = curId.toString() // 如果是初始化阶段,不触发date-search if (!isInitialized.value) { isInitialized.value = true return } // 向左滑动加载前周 if (curId === weekArray.value[0]) { weekArray.value.unshift(curId - 1) // 限制最大数量(可选) if (weekArray.value.length > 10) weekArray.value.pop() } // 向右滑动加载下周 else if (curId === weekArray.value[weekArray.value.length - 1]) { weekArray.value.push(curId + 1) // 限制最大数量(可选) if (weekArray.value.length > 10) weekArray.value.shift() } // 触发数据更新 const curDate = addWeeks(baseDate.value, curId) emit('date-search', ...getDateRange(curDate)) } // 创建节流版本 const throttledSwiperChange = throttle(onSwiperChange, 300, { leading: true, trailing: false }) // 向左箭头查询上周课表列表数据 // 获取上周的开始和结束时间 const getLastWeek = () => { //时间格式化为YYYY-MM-DD const lastWeek = subWeeks(baseDate.value, 1) const start = format(startOfWeek(lastWeek, { weekStartsOn: 1 }), 'yyyy-MM-dd') const end = format(endOfWeek(lastWeek, { weekStartsOn: 1 }), 'yyyy-MM-dd') return { start, end } } //获取下周的开始和结束时间 const getNextWeek = () => { const nextWeek = addWeeks(baseDate.value, 1) const start = format(startOfWeek(nextWeek, { weekStartsOn: 1 }), 'yyyy-MM-dd') const end = format(endOfWeek(nextWeek, { weekStartsOn: 1 }), 'yyyy-MM-dd') return { start, end } } // const handleArrowLeft = () => { // // 先获取时间范围(只调用次) // const newId = Math.min(...weekArray.value) - 1 // weekArray.value.unshift(newId) // currentItemId.value = newId.toString() // // 发送单个请求 // getCalendarStatistics({ // beginTime: `${start} 00:00:00`, // endTime: `${end} 23:59:59`, // orgId: orgId.value, // userId: userInfo.userId // }).then(res => { // formattedDate.value = `${start} - ${end}` // }) // // 触发父组件更新(只需要触发次) // emit('date-search', start, end) // } // 向右箭头下周 // const handleArrowRight = () => { // // 先获取时间范围(只调用次) // const { start, end } = getNextWeek() // //下周 // baseDate.value = addWeeks(baseDate.value, 1) // // 触发父组件更新(只需要触发次) // //发送单个请求 // getCalendarStatistics({ // beginTime: `${start} 00:00:00`, // endTime: `${end} 23:59:59`, // orgId: orgId.value, // userId: userInfo.userId // }).then(res => { // formattedDate.value = `${start} - ${end}` // }) // // 触发父组件更新(只需要触发次) // emit('date-search', start, end) // } const currentSelected = ref(formatDate1(new Date())) // 初始化为字符串格式 // 选择期 // 修改后的selectDay方法 const selectDay = day => { currentSelected.value = formatDate1(day.date) // 确保存储字符串格式 emit('date-change', currentSelected.value) // 调试志 console.log('当前选中:', currentSelected.value) console.log('点击期:', formatDate1(day.date)) } // 格式化期取当前期的周的开始和结束 const formattedDate = computed(() => { const currentWeekIndex = currentItemId.value const currentWeek = weekDateObject.value.obj[currentWeekIndex] if (!currentWeek) return '' const monday = currentWeek[0].date const sunday = currentWeek[6].date // 格式化期为 "YYYY年MM月DD" 格式 const formatDate = date => { const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') return `${year}年${month}月${day}` } // 格式化结束期为 "MM月DD" 格式 const formatEndDate = date => { const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') return `${month}月${day}` } return `${formatDate(monday)} - ${formatEndDate(sunday)}` }) const formatDate = dateString => { const date = new Date(dateString) return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}` // 格式化为 YYYY年MM月 // return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}`; // 格式化为 YYYY年MM月DD } </script> <style scoped> .calendar-container { position: relative; background: #fff; border-radius: 32rpx; /* box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); */ } .header { padding: 24rpx; border-bottom: 1rpx solid #eee; } .year-month { font-size: 32rpx; color: #333; font-weight: bold; } .swiper-container { /* padding-top: 90rpx; */ height: 180rpx; } .swiper-item { height: 100%; } .week-container { display: flex; justify-content: space-around; align-items: center; padding: 0 20rpx; gap: 5rpx; height: 100%; } .day-item { display: flex; flex-direction: column; align-items: center; flex: 1; /* 关键修改:等分容器宽度 */ height: 120rpx; border-radius: 16rpx; transition: all 0.2s; font-weight: 500; color: #4a4a4a; } .weekday { font-size: 20rpx; color: #4a4a4a; margin-bottom: 8rpx; } .day-number { font-size: 28rpx; color: #333; padding: 0 10rpx; border-radius: 12rpx; margin-bottom: 10rpx; } /* .current { background: #5e94ff !important; color: #fff !important; border: 1rpx solid #3a6ccc !important; } */ .today { background: #83c3ff; color: #333; } .today { /* background: #83c3ff; */ /* border: 1rpx solid #5e94ff; */ } /* .today .day-number { color: #328add; font-weight: bold; } */ .current { background: #5e94ff !important; color: #fff !important; } .current .weekday, .current .day-number { color: #fff !important; } .date-change-btn { /* position: absolute; top: 10rpx; left: 24rpx; */ /* padding: 0 20rpx; */ font-weight: bold; font-size: 26rpx; color: #000000; line-height: 70rpx; height: 70rpx; /* margin-left: 40rpx; */ /* background: #edf6ff; */ border-radius: 20rpx 20rpx 20rpx 20rpx; } .person-img { position: absolute; right: 60rpx; top: -128rpx; width: 200rpx; } </style>能不能根据已上代码重新写这个组件,样式布局不变,props的条件不变,就是把滑动历的功能和点击上周和下周这两个共嗯那个每次改变保持同步可以更改const weekArray = ref([-1, 0, 1]) 的初始值,只要滑动和切换保持致就可以,
07-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值