calc()函数 css中用100%的宽度/高度,减去50px??

CSS calc()函数实战
本文详细介绍了如何使用CSS中的calc()函数来精确控制元素高度,特别是在需要让子元素填满父元素剩余空间的场景中。通过实例展示了如何计算并设置高度,使布局更加灵活和响应式。

做项目的时候常常用遇到下面这种情况:
要让B盒子占满 A盒子剩下的部分也就是黄框区域,这时候我们可以设置 B盒子 高为 100% 或者是100vh减去上面的 A盒子的高度50px。

只需设置样式使用calc() 函数,它支持 “+”, “-”, “*”, “/” 运算;
注意:

运算符前后都需要保留一个空格,例如:width: calc(100% - 10px);
任何长度值都可以使用calc()函数进行计算;
calc()函数使用标准的数学运算优先级规则;
它支持 “+”, “-”, “*”, “/” 运算

        B盒子 {
            //height: calc(100vh - 50px);
            height: calc(100% - 50px);
        }
1234
扩展: vh:相对于视口的高度。视口被均分为100单位的vh
————————————————
版权声明:本文为优快云博主「最初都是小白」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/Originally_M/article/details/100105309

<script setup lang="ts"> import {ref, nextTick, computed} from 'vue' definePage({ name: 'AI', style: { navigationBarTitleText: 'AI', navigationStyle: 'custom' }, }) // 弹出框 const show = ref(false) const onClickLeft = () => { uni.navigateBack() } // ai 模型选择 const aiForm = ref({ model: 'DeepSeek', iconUrl: '/static/AI/deepseek.png', width: 35, height: 22, }) // ai 模型列表 const aiModel = ref([{ id: 1, name: 'DeepSeek V3.2 Think', iconUrl: '/static/AI/deepseek.png', width: 35, height: 22, }, { id: 2, name: 'DeepSeek V3.1 Think', iconUrl: '/static/AI/deepseek.png', width: 35, height: 22, }, { id: 3, name: 'ERNIE 4.5 Turbo VL', iconUrl: '/static/AI/ernie.png', width: 22, height: 22, }, { id: 4, name: 'ERNIE X1.1', iconUrl: '/static/AI/ernie.png', width: 22, height: 22, }]) // 选择模型 const selectModel = () => { show.value = true } // 变更模型 const changeModel = (value: any) => { aiForm.value.model = value.name aiForm.value.iconUrl = value.iconUrl aiForm.value.width = value.width aiForm.value.height = value.height show.value = false } // 聊天消息列表 const messages = ref<any>([ {role: 'ai', content: '你好!我是你的 AI 助手,请问有什么可以帮您?'} ]) // 输入框内容 const inputMessage = ref('') // 是否正在加载 AI 回复 const loading = ref(false) // 自动滚动到最新消息 const scrollIntoView = computed(() => { return `msg-${messages.value.length - 1}` }) // 发送消息 const handleSend = async () => { if (!inputMessage.value.trim() || loading.value) return // 添加用户消息 messages.value.push({ role: 'user', content: inputMessage.value }) inputMessage.value = '' // 显示加载状态 loading.value = true // 确保视图更新后滚动到底部 await nextTick() scrollToBottom() // 模拟调用 AI 接口 try { const response = await callAiApi(messages.value.slice(-1)[0].content) messages.value.push({ role: 'ai', content: response }) } catch (error) { messages.value.push({ role: 'ai', content: '抱歉,AI 服务暂时不可用,请稍后再试。' }) } finally { loading.value = false await nextTick() scrollToBottom() } } // 模拟 AI 接口请求(实际项目中替换为真实 API) const callAiApi = (prompt: any) => { return new Promise((resolve) => { setTimeout(() => { resolve(`这是对“${prompt}”的回答。AI正在模拟中...`) }, 1200) }) // 实际开发中应使用如下方式: // return new Promise((resolve, reject) => { // uni.request({ // url: 'https://your-ai-api.com/chat', // method: 'POST', // data: {prompt}, // success: (res) => { // if (res.statusCode === 200) { // resolve(res.data.response) // } else { // reject(new Error('AI request failed')) // } // }, // fail: reject // }) // }) } // 滚动到底部 const scrollToBottom = () => { return nextTick() } // 监听窗口高度变化(适配键盘弹起) const windowHeight = ref(uni.getSystemInfoSync().windowHeight) onMounted(() => { // 监听窗口尺寸变化(小程序键盘弹起会触发) uni.onWindowResize((res) => { windowHeight.value = res.size.windowHeight }) }) // 弹出框关闭时清空 const handleClose = () => { show.value = false } </script> <template> <view class="container" :style="{ height: windowHeight + 'px' }"> <wd-navbar custom-class="ai-navbar" fixed custom-style="background-color: transparent !important; height: 140rpx !important;" safeAreaInsetTop> <template #left> <view class="left-button" @click="onClickLeft"> <wd-icon name="arrow-left1" size="22px"></wd-icon> </view> </template> <template #title> <view class="title-box" @click="selectModel"> <wd-img :width="aiForm.width" :height="aiForm.height" :src="aiForm.iconUrl"/> <span class="title-text">{{ aiForm.model }}</span> <wd-icon name="caret-down-small" size="18px"></wd-icon> </view> </template> </wd-navbar> <view style="height: calc(140rpx + 54px)"> </view> <view class="chat-container"> <!-- 消息列表 --> <scroll-view class="message-list" scroll-y :scroll-into-view="scrollIntoView" :style="{ height: windowHeight - 120 + 'px' }" > <!-- 单条消息 --> <view v-for="(msg, index) in messages" :key="index" :id="'msg-' + index" :class="['message-item', msg.role === 'user' ? 'message-item-user' : 'message-item-ai']" > <!-- 用户消息:右对齐,带头像 --> <view v-if="msg.role === 'user'" class="user-message-container"> <image src="/static/user-avatar.png" class="avatar"/> <text class="user-message-text">{{ msg.content }}</text> </view> <!-- AI 消息:左对齐,带头像 --> <view v-else class="ai-message"> <image src="/static/avatar.png" class="avatar"/> <text class="ai-message-text">{{ msg.content }}</text> </view> </view> <!-- AI 正在输入状态 --> <view v-if="loading" class="ai-message"> <image src="/static/avatar.png" class="avatar"/> <text class="message-text">AI思考中...</text> </view> </scroll-view> <!-- 输入区域 --> <view class="input-area"> <input v-model="inputMessage" class="input-field" type="text" placeholder="请输入您的问题..." @confirm="handleSend" :adjust-position="true" > </input> </view> </view> </view> <wd-popup v-model="show" position="top" transition="fade-down" custom-style="border-radius:32rpx;top:200rpx;left:40rpx;right:40rpx;padding-bottom: 30rpx;" @close="handleClose"> <wd-card title="模型选择" class="model-container"> <view v-for="item in aiModel" :key="item.id" class="model-item" @click="changeModel(item)"> <wd-img :width="item.width" :height="item.height" :src="item.iconUrl"/> <span class="title-text">{{ item.name }}</span> </view> </wd-card> </wd-popup> </template> <style scoped lang="scss"> :deep(.wd-navbar.is-border:after) { background: none !important; } .left-button { border-radius: 50px; height: 80rpx; width: 80rpx; background-color: rgba(172, 172, 172, 0.13); display: flex; align-items: center; justify-content: center; } .title-box { display: flex; height: 100%; align-items: center; justify-content: center; } .title-text { margin-left: 10rpx; font-size: 28rpx; } .model-container { width: 500rpx; height: 460rpx; display: flex; justify-content: center; align-items: center; font-size: 40rpx; border-radius: 32rpx; } .model-item { height: 100rpx; border-radius: 15rpx; margin-bottom: 20rpx; border: #9e56f6 1rpx solid; display: flex; align-items: center; justify-content: center; } .container { //margin-top: calc(140rpx + 54px); } :deep(.page-wraper) { min-height: 0 !important; } /* 聊天区域 根容器:全屏高度,弹性布局垂直排列 */ .chat-container { display: flex; flex-direction: column; width: 100%; height: calc(100vh - 140rpx - 54px); overflow-x: hidden; box-sizing: border-box; } /* 消息列表区域:自动填充剩余空间 */ .message-list { flex: 1; /* 自动伸缩以填满父容器减去输入框的高度 */ padding: 20rpx; overflow-y: auto; /* 垂直滚动 */ overflow-x: hidden; /* 防止意外横向滚动 */ box-sizing: border-box; } /* 每一条消息容器 */ .message-item { display: flex; margin-bottom: 15rpx; width: 100%; box-sizing: border-box; } /* 每一条消息容器 */ .message-item { display: flex; margin-bottom: 15rpx; width: 100%; box-sizing: border-box; } /* 用户消息整体右对齐 */ .message-item-user { justify-content: flex-end; /* 关键:让整条消息靠右 */ } /* AI 消息左对齐 */ .message-item-ai { justify-content: flex-start; } /* 用户消息容器(仅内容排列)*/ .user-message-container { display: flex; align-items: flex-start; gap: 16rpx; max-width: 100%; justify-content: flex-end; /* 整体右对齐 */ flex-direction: row-reverse; /* 视觉上:气泡在左,头像在右 */ } /* 用户消息气泡 */ .user-message-text { padding: 16rpx 24rpx; border-radius: 30rpx 30rpx 0 30rpx; /* 左下角无弧度 */ max-width: 70%; word-wrap: break-word; word-break: break-word; font-size: 28rpx; box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05); line-height: 1.5; } /* AI 消息气泡 */ .ai-message-text { padding: 16rpx 24rpx; border-radius: 30rpx 30rpx 30rpx 0; /* 左下角无弧度 */ max-width: 70%; word-wrap: break-word; word-break: break-word; font-size: 28rpx; box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05); line-height: 1.5; } /* AI 消息整体容器:左对齐,含头像和文本 */ .ai-message { display: flex; align-items: flex-start; gap: 16rpx; /* 头像与文字间距 */ max-width: 100%; } /* AI 头像样式 */ .avatar { width: 60rpx; height: 60rpx; border-radius: 50%; /* 圆形 */ border: #b559ff 1rpx solid; flex-shrink: 0; /* 防止被压缩 */ } /* 输入区域:固定在底部,留出安全区 */ .input-area { display: flex; align-items: center; padding: 20rpx; position: relative; z-index: 999; width: 100%; box-sizing: border-box; /* 安全区适配:iOS 设备底部留白 */ padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); /* 或使用 constant,在老版本 iOS 中兼容 */ padding-bottom: calc(constant(safe-area-inset-bottom) + 20rpx); padding-bottom: calc(env(safe-area-inset-bottom, 20rpx) + 20rpx); } /* 输入框样式(可自由调整) */ .input-field { flex: 1; height: 120rpx; line-height: 80rpx; border: 1rpx solid #da34ff; border-radius: 25rpx; padding: 0 30rpx; font-size: 28rpx; max-width: 100%; margin-right: 20rpx; overflow: hidden; word-break: break-word; margin-bottom: 10rpx; } </style> 键盘唤起时,内容会根据键盘的高度给撑出去
最新发布
11-07
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值