TextAlign 与 margin:0 auto

本文详细解释了CSS中text-align:center与margin:0 auto两种不同的居中方式,并探讨了它们在不同浏览器中的表现差异,以及如何正确应用于块元素与内联元素。

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

margin:0 auto 与 text-align:center 的区别

基本概念:

1.text-align: 属性规定元素中的文本的水平对齐方式; 

       该属性通过指定行框与哪个点对齐,从而设置块级元素内文本的水平对齐方式;

       一般情况下设置文本对齐方式的时使用此属性。支持值 justify。

  Example: div { text-align: left; }  //文本居左对齐

  注释:所有浏览器都支持 text-align 属性;任何的版本的 Internet Explorer (包括 IE8)都不支持属性值 "inherit"。

2.margin 是设置对象四边的外延边距,被称为外补丁或外边距。

  Example: div { margin: 20px 10px 30px 40px; }  // 表示对象外边距,顶20px、右10px、下30px、左40px

区别如下:

1.text-align:center 设置文本或img标签等一些内联对象(或与之类似的元素)的居中。

2.margin:0 auto 设置块元素(或与之类似的元素)的居中。

这两个属性IE与FF的理解也有所不同。我们设置一个段落P,在段落内存在一个图片img标签。  

注意:

1.当设置body{text-align:center;}。 在IE中,段落P,图片img同时实现了居中对齐,也就是说text-align:center;同时作用于元素p与元素img。 在FF中,段落P,没有能实现居中对齐,而图片img实现了居中对齐,也就是说text-align:center;作用于img标签,而段落p标签没有起到居中的作用。

2.当设置段落 p {margin:0 auto;}。 在IE与FF中,段落P均实现了居中对齐。图片img由于不是作用对象,所以不会居中对齐。

有三种情况需要说明: 1.margin:0 auto;的选择器是作用对象,如div,p,而不是body。如果设置:body { margin:0 auto; }将不会达到任何效果,除非你定义body的宽度,那将会让body内的元素产生位置变化。如我们设置body宽度为500px。对p段落不作任何设置, 我们最大化窗口将会看到段落并非处于窗口的最左上角。 2.设置段落 p {text-align:center;} 将要实现的并不是段落本身的对齐方式,而是段落内元素居中对齐。 3. 设置图片标签img {margin:0 auto;} ,就犯了一个小错误,img类于内联对象,不可以设置图片img标签的margin属性,如果一定要设置,那么先将它的属性转变为块元素,如下面的代 码:img {display:block; margin:0 auto;}

知识点:块元素,内联元素的区别。

<div id="tabbar" v-if="true" class="tabbar" :style='{"padding":"30px 0","boxShadow":"0 0 6px ","margin":"30px 0 0 0","borderColor":"rgba(255, 255, 255, 0.3)","backgroundColor":"rgba(6, 82, 121, 1)","borderRadius":"0","borderWidth":"0","borderStyle":"solid"}' style="position: relative;z-index: 999;"> <img v-if='false' :style='{"boxShadow":"0 0 6px rgba(255,0,0,.8)","margin":"0 auto","borderColor":"rgba(0,0,0,.3)","borderRadius":"100%","borderWidth":"1px","width":"44px","borderStyle":"solid","height":"44px"}' style='display: block;' src='http://codegen.caihongy.cn/20201024/ed5e326ca66f403aa3197b5fbb4ec733.jpg'/> <div :style='{"padding":"0 10px","boxShadow":"0 0 6px rgba(255,0,0,.8)","margin":"10px auto","borderColor":"rgba(0,0,0,1)","backgroundColor":"#000","color":"#fff","textAlign":"center","borderRadius":"0","borderWidth":"0","width":"100%","lineHeight":"32px","fontSize":"14px","borderStyle":"solid"}' class="company"></div> <div :style='{"padding":"0 10px","boxShadow":"0 0 6px rgba(255,0,0,0)","margin":"10px auto","borderColor":"rgba(0,0,0,1)","backgroundColor":"#000","color":"#fff","textAlign":"center","borderRadius":"0","borderWidth":"0","width":"100%","lineHeight":"32px","fontSize":"14px","borderStyle":"solid"}' class="record"></div> <div :style='{"padding":"0 10px","boxShadow":"0 0 0px ","margin":"10px auto 30px auto","borderColor":"rgba(6, 82, 121, 1)","backgroundColor":"rgba(6, 82, 121, 1)","color":"#fff","textAlign":"center","borderRadius":"0","borderWidth":"1px 0","width":"40%","lineHeight":"32px","fontSize":"14px","borderStyle":"solid"}' class="desc">电话:4008000000 | 邮箱:support@21epub.com </div> </div>
05-30
切换到查看所有时出现的问题,手机上显示不全然后图标变形了都,重新弄当我点击查看全部之后出现像图三那样的样式,柱状图用canvas弄出来,对下面的代码进行修改,只改动涉及到底哪个地方,其他的不要动,代码整体格式不变 <template> <view class="daily-details-container"> <!-- 学校及日期信息 --> <view class="school-info"> <view class="school-name-container"> <text class="school-name">{{ schoolName }}</text> <navigator url="/pages/allreport/reading-situation"> <text class="read-info" @click="viewReadInfo">阅读情况</text> </navigator> </view> <view class="date-info"> <text class="date-label">汇报日期</text> <text class="report-date">{{ reportDate }}</text> </view> </view> <view class="content-body"> <!-- 主标题 --> <view class="content-title"> {{ schoolName }}-{{ formatDate(reportDate) }}(周一) </view> <!-- 水表模块:仅折叠图片,备注和状态常驻 --> <view class="collapse-header1" @click="toggleCollapse1('waterMeter')"> <image class="icon" src="/static/water-meter-diagram.svg" mode="aspectFit"></image> <text class="collapse-title">水表图片</text> <image class="arrow" mode="aspectFit" :style="{ transform: showWaterMeterImages ? 'rotate(180deg)' : 'rotate(0deg)' }" src="/static/arrow-down.svg"></image> </view> <!-- 折叠内容:仅图片 --> <view class="collapse-content" v-show="showWaterMeterImages"> <view class="meter-images"> <image class="meter-img" v-for="(img, index) in meterImages" :key="index" :src="img" mode="aspectFill"></image> </view> </view> <!-- 日报图片模块 --> <view class="collapse-item"> <!-- 常驻内容:备注 + 状态 --> <view class="collapse-header2"> <view class="remark-label">水表备注</view> <view class="operation">{{ operationText }}</view> </view> <view class="collapse-header3" @click="toggleCollapse2('dailyReport')"> <image class="icon" src="/static/water-meter-diagram.svg" mode="aspectFit"></image> <text class="collapse-title">日报图片</text> <image class="arrow" mode="aspectFit" :style="{ transform: showDailyReportContent ? 'rotate(180deg)' : 'rotate(0deg)' }" src="/static/arrow-down.svg"></image> </view> <view class="collapse-content" v-show="showDailyReportContent"> <text>日报图片内容展示...</text> </view> </view> <!-- 历史总用量 --> <view class="usage-info"> <view class="usage-left"> <!-- 根据状态切换标题 --> <text class="usage-label">{{ viewAll ? '历史总用量' : '当日总用量' }}</text> <!-- 日期格式处理,根据需求调整,这里简单截取 --> <text class="usage-date">{{ viewAll ? reportDate.slice(0,0) : reportDate }}</text> </view> <view class="usage-center"> <!-- 根据状态切换展示的数据 --> <text class="usage-value">{{ viewAll ? ' ' : todayUsageData[0].value }}</text> </view> <view class="usage-right"> <button class="view-all" @click="toggleView"> <image :src="viewAll ? '/static/all-see.svg' : '/static/see-today.svg'" mode="widthFix" class="seeall" ></image> <text class="see">{{ viewAll ? '仅看当天' : '查看所有' }}</text> </button> <!-- 柱状图容器,根据状态控制显示 --> <view class="chart-container" v-show="viewAll"> <view class="chart"> <view v-for="(item, index) in usageData" :key="index" class="bar-wrapper" > <view class="bar" :style="{ height: `${item.height}%` }" > <text class="bar-value">{{ item.value }}</text> </view> <text class="bar-label">{{ item.label }}</text> </view> </view> </view> </view> </view> <!-- 水表数据表格 --> <view class="table-container"> <view class="table-header"> <text class="table-th">序号</text> <text class="table-th">水表</text> <text class="table-th">抄表数据</text> <text class="table-th">操作</text> </view> <view class="table-body"> <view class="table-tr" v-for="(item, index) in meterData" :key="index"> <text class="table-td">{{ index + 1 }}</text> <text class="table-td">{{ item.meterName }}</text> <text class="table-td">{{ item.readingData }}</text> <view class="history-btn" @click="viewHistory(item)"> <image src="/static/blue-arrow-down.svg" mode="" class="history-image"></image> <text class="see" >历史</text> </view> </view> </view> </view> <view class="pagination"> <image class="arrow arrow-left" src="/static/left.svg" mode="aspectFit"></image> <text class="page-info">{{ currentPage }}/{{ totalPages }}</text> <image class="arrow arrow-right" src="/static/right.svg" mode="aspectFit"></image> </view> <view class="end"> <text>日报备注</text> <text>今日抄表记录并分析用水情况,继续观察</text> </view> </view> </view> </template> <script setup> import { ref } from 'vue'; import { onLoad } from '@dcloudio/uni-app'; // 数据定义 const schoolName = ref('广西机电工程学校'); const reportDate = ref('2025-06-16'); const operationText = ref('继续观察'); const showWaterMeterImages = ref(false); const showDailyReportContent = ref(false); const viewAll = ref(true); // 初始显示"查看所有"状态 // 历史用量数据 const usageData = ref([ { value: '945', label: '6/10', height: 95 }, { value: '950', label: '6/11', height: 96 }, { value: '920', label: '6/12', height: 93 }, { value: '966', label: '6/13', height: 98 }, { value: '818', label: '6/14', height: 84 }, { value: '816', label: '6/15', height: 83 }, { value: '817', label: '6/16', height: 83 } // 添加当前日期数据 ]); // 当天用量数据 const todayUsageData = ref([ { value: '817', label: '6/16', height: 83 } ]); // 水表图片地址 const meterImages = ref([ '/static/all-see.svg', '/static/all-see.svg', '/static/meter3.png', '/static/meter4.png' ]); const meterData = ref([ { meterName: "市政表1", readingData: "552491" }, { meterName: "市政表2", readingData: "45972" }, { meterName: "市政表3", readingData: "6081" }, { meterName: "市政表4", readingData: "13947" } ]); const currentPage = ref(1); const totalPages = ref(1); // 页面加载逻辑 onLoad((options) => { if (options.schoolName) schoolName.value = decodeURIComponent(options.schoolName); if (options.reportDate) reportDate.value = options.reportDate; }); // 方法定义 const viewReadInfo = () => { uni.showToast({ title: "查看阅读情况功能待实现", icon: "none" }); }; // 切换折叠状态 const toggleCollapse1 = (type) => { if (type === "waterMeter") { showWaterMeterImages.value = !showWaterMeterImages.value; } }; const toggleCollapse2 = (type) => { if (type === "dailyReport") { showDailyReportContent.value = !showDailyReportContent.value; } }; const formatDate = (dateStr) => dateStr.split(' ')[0]; const viewHistory = (item) => { uni.showToast({ title: `查看${item.meterName}历史数据功能待实现`, icon: "none" }); }; // 切换查看模式 const toggleView = () => { viewAll.value = !viewAll.value; }; </script> <style scoped> /* 完整的CSS样式 (style scoped部分) */ .daily-details-container { padding: 15px; background-color: #f3f3f3; min-height: 100vh; } /* 顶部标题栏 */ .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding: 0 18rpx; } .page-title { font-size: 18px; font-weight: bold; color: #333; } .export-pdf { color: #007aff; font-size: 14px; } /* 学校信息模块 */ .school-info { margin-bottom: 15px; background-color: #fff; padding: 15px; border-radius: 16rpx; } .school-name-container { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } .school-name { font-size: 20px; font-weight: bold; color: #333; } .read-info { color: #007aff; font-size: 16px; font-weight: bold; } .date-info { display: flex; justify-content: space-between; align-items: center; } .date-label { color: #999; font-size: 16px; margin-right: 5px; font-weight: normal; } .report-date { font-size: 16px; color: #111; font-weight: bold; } .content-body { background-color: #fff; border-radius: 15rpx; padding: 15px; } /* 主标题 */ .content-title { font-size: 16px; font-weight: bold; margin-bottom: 40rpx; color: #333; padding-bottom: 18px; border-bottom: 1px solid #f0f0f0; } /* 折叠模块通用样式 */ .collapse-item { margin-bottom: 15px; border-radius: 5px; overflow: hidden; border-bottom: 1rpx solid #eee; } .collapse-header1 { display: flex; align-items: center; padding: 10px 15px; background-color: #FFFFFF; border-radius: 4px; cursor: pointer; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); } .collapse-header2 { display: block; justify-content: space-between; align-items: center; padding: 10px 15px; background-color: #FFFFFF; border-radius: 4px; cursor: pointer; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); } .collapse-header3 { display: flex; justify-content: space-between; align-items: center; padding: 10px 15px; background-color: #FFFFFF; border-radius: 4px; cursor: pointer; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); } .icon { width: 20px; height: 20px; margin-right: 8px; } .collapse-title { flex: 1; font-size: 14px; color: #666666; font-weight: bold; } .arrow { width: 16px; height: 16px; color: #999999; transition: transform 0.3s ease; } /* 水表模块:常驻内容(备注 + 状态) */ .permanent-content { padding: 10px; background-color: #fff; border-bottom: 1px solid #eee; /* 图片区分隔 */ } .remark-label { color: #999; font-size: 14px; margin-bottom: 15px; } .operation { font-size: 16px; font-weight: bold; color: #55aaff; margin-bottom: 30rpx; } /* 水表模块:折叠内容(仅图片) */ .collapse-content { padding: 10px; background-color: #fff; } .meter-images { display: flex; flex-wrap: wrap; justify-content: space-between; } .meter-img { width: 48%; margin-bottom: 10px; border-radius: 5px; } .usage-info { display: flex; align-items: center; background-color: #fff; border-radius: 8px; padding: 15px; margin: 20px 0; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); border: 1rpx solid #F2F8FF; } .usage-left { flex: 1; display: flex; flex-direction: column; } .usage-label { font-size: 16px; color: #000; margin-bottom: 4px; font-weight: bold; } .usage-date { font-size: 14px; color: #666; } .usage-center { flex: 1; text-align: center; } .usage-value { font-size: 24px; font-weight: 500; color: #55aaff; } .usage-right { margin-left: auto; } .view-all { display: flex; align-items: center; gap: 6px; padding: 6px 12px; font-size: 14px; color: #007AFF; background-color: #F2F8FF; border: 1px solid #007AFF; border-radius: 20px; line-height: 1.5; -webkit-appearance: none; appearance: none; } .seeall { width: 16px; height: 16px; display: inline-block; vertical-align: middle; } .see { vertical-align: middle; } /* 表格模块 */ .table-container { margin-top: 15px; border-radius: 10px; border-bottom: 1rpx solid #f5f5f5; background-color: #fff; } .table-header { display: flex; background-color: #f5f5f5; border-radius: 10rpx; } .table-th { flex: 1; text-align: center; padding: 10px; font-weight: bold; } .table-body { display: block; justify-content: space-between; } .table-tr { display: flex; margin-top: 20rpx; margin-bottom: 40rpx; } .table-td { flex: 1; text-align: center; padding: 0px; flex-basis: 0%; } .history-btn { flex: 1; text-align: center; font-size: 14px; color: #55aaff; border-radius: 20px; background-color: #dcf3ff; } .history-image { width: 12px; height: 12px; margin-right: 10rpx; vertical-align: middle; } .pagination { display: flex; align-items: center; justify-content: center; background-color: #FFFFFF; padding: 8px 16px; margin: 15px 0; } .page-info { margin: 0 1px; font-size: 16px; color: #333333; font-weight: normal; } .arrow { width: 24px; height: 24px; margin: 0 4px; opacity: 0.5; cursor: pointer; display: flex; align-items: center; justify-content: center; } .arrow-left { margin-left: 18px; background-color: #ccc; } .arrow-right { margin-right: 18px; background-color: #ccc; } .arrow:disabled { opacity: 0.3; cursor: not-allowed; } .end { padding: 10px; background-color: transparent; } .end text:first-child { font-size: 14px; color: #999; margin-top: 18px; margin-bottom: 18px; display: block; } .end text:last-child { font-size: 16px; color: #4A90E2; line-height: 1.5; font-weight: bold; } /* 新增:柱状图样式 */ .chart-container { padding: 10px; margin-top: 10px; width: 100%; overflow-x: auto; } .chart { display: flex; align-items: flex-end; height: 180px; min-width: 500px; /* 防止内容过窄 */ border-bottom: 1px solid #eee; border-left: 1px solid #eee; padding: 0 10px; box-sizing: border-box; } .bar-wrapper { display: flex; flex-direction: column; align-items: center; flex: 1; margin: 0 5px; } .bar { width: 20px; background-color: #55aaff; border-radius: 4px 4px 0 0; display: flex; justify-content: center; position: relative; transition: height 0.3s ease; } .bar-value { position: absolute; top: -20px; color: #333; font-size: 12px; font-weight: bold; } .bar-label { margin-top: 8px; font-size: 12px; color: #666; text-align: center; white-space: nowrap; } </style>
06-19
<!DOCTYPE html> <html> <head> <title>伯德图(Bode Plot)原理示意图</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background: #f5f7fa; } .container { max-width: 1000px; margin: 0 auto; } h1 { text-align: center; color: #2c3e50; margin-bottom: 10px; } .subtitle { text-align: center; color: #7f8c8d; margin-bottom: 30px; } .plot-container { display: flex; flex-direction: column; gap: 30px; } .plot { background: white; border-radius: 10px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); padding: 20px; position: relative; } .plot-title { font-size: 18px; font-weight: bold; margin-bottom: 15px; color: #34495e; } .canvas-container { position: relative; } canvas { border: 1px solid #ecf0f1; border-radius: 5px; } .legend { display: flex; gap: 20px; margin-top: 15px; justify-content: center; } .legend-item { display: flex; align-items: center; } .color-box { width: 20px; height: 20px; margin-right: 8px; border-radius: 3px; } .controls { display: flex; gap: 15px; margin-top: 20px; justify-content: center; } .control-group { display: flex; flex-direction: column; } label { margin-bottom: 5px; font-size: 14px; color: #34495e; } input[type="range"] { width: 200px; } .explanation { margin-top: 30px; background: #e8f4f8; padding: 20px; border-radius: 10px; border-left: 4px solid #3498db; } .explanation h3 { color: #2c3e50; margin-top: 0; } .explanation ul { padding-left: 20px; } .explanation li { margin-bottom: 10px; line-height: 1.6; } .key-point { color: #e74c3c; font-weight: bold; } </style> </head> <body> <div class="container"> <h1>伯德图(Bode Plot)原理分析</h1> <div class="subtitle">幅度响应和相位响应的频率特性图示</div> <div class="plot-container"> <div class="plot"> <div class="plot-title">幅度响应图 (Magnitude Plot)</div> <div class="canvas-container"> <canvas id="magnitudePlot" width="900" height="300"></canvas> </div> </div> <div class="plot"> <div class="plot-title">相位响应图 (Phase Plot)</div> <div class="canvas-container"> <canvas id="phasePlot" width="900" height="300"></canvas> </div> </div> <div class="legend"> <div class="legend-item"> <div class="color-box" style="background-color: #3498db;"></div> <span>系统实际响应</span> </div> <div class="legend-item"> <div class="color-box" style="background-color: #e74c3c;"></div> <span>渐近线近似</span> </div> <div class="legend-item"> <div class="color-box" style="background-color: #27ae60;"></div> <span>截止频率点</span> </div> </div> <div class="controls"> <div class="control-group"> <label for="poleFreq">极点频率 (Hz): <span id="poleValue">1000</span></label> <input type="range" id="poleFreq" min="100" max="5000" value="1000" step="100"> </div> <div class="control-group"> <label for="zeroFreq">零点频率 (Hz): <span id="zeroValue">2000</span></label> <input type="range" id="zeroFreq" min="500" max="5000" value="2000" step="100"> </div> </div> </div> <div class="explanation"> <h3>伯德图核心概念解析:</h3> <ul> <li><span class="key-point">伯德图组成</span>:由幅度图(dB-频率)和相位图(度-频率)组成,均采用半对数坐标系</li> <li><span class="key-point">渐近线近似</span>:用直线段近似表示系统频率响应,在极点处-20dB/decade下降,零点处+20dB/decade上升</li> <li><span class="key-point">截止频率</span>(图中绿点):幅度下降3dB对应的频率点,相位变化45°的位置</li> <li><span class="key-point">稳定性分析</span>:通过相位裕度(PM)和增益裕度(GM)判断系统稳定性</li> <li><span class="key-point">工程应用</span>:用于滤波器设计、控制系统稳定性分析、放大器频率补偿等</li> </ul> </div> </div> <script> // 获取Canvas元素和上下文 const magCanvas = document.getElementById('magnitudePlot'); const phaseCanvas = document.getElementById('phasePlot'); const magCtx = magCanvas.getContext('2d'); const phaseCtx = phaseCanvas.getContext('2d'); // 获取控制滑块 const poleSlider = document.getElementById('poleFreq'); const zeroSlider = document.getElementById('zeroFreq'); const poleValue = document.getElementById('poleValue'); const zeroValue = document.getElementById('zeroValue'); // 初始参数 let poleFrequency = 1000; let zeroFrequency = 2000; // 更新显示值 poleSlider.addEventListener('input', function() { poleFrequency = parseInt(this.value); poleValue.textContent = poleFrequency; drawBodePlots(); }); zeroSlider.addEventListener('input', function() { zeroFrequency = parseInt(this.value); zeroValue.textContent = zeroFrequency; drawBodePlots(); }); // 计算传递函数响应 function calculateResponse(freq) { // 一阶系统传递函数: H(s) = (s/zero + 1) / (s/pole + 1) const s = 2 * Math.PI * freq * 1j; // 计算幅度响应 (dB) const magnitude = 20 * Math.log10( Math.sqrt(1 + (freq/zeroFrequency)**2) / Math.sqrt(1 + (freq/poleFrequency)**2) ); // 计算相位响应 (度) const phase = ( Math.atan(freq/zeroFrequency) - Math.atan(freq/poleFrequency) ) * 180 / Math.PI; return { magnitude, phase }; } // 计算渐近线 function calculateAsymptotes(freq) { let magAsymptote = 0; let phaseAsymptote = 0; if (freq < Math.min(poleFrequency, zeroFrequency)) { magAsymptote = 0; phaseAsymptote = 0; } else if (freq >= Math.min(poleFrequency, zeroFrequency) && freq < Math.max(poleFrequency, zeroFrequency)) { // 在极点和零点之间 if (poleFrequency < zeroFrequency) { magAsymptote = -20 * Math.log10(freq/poleFrequency); phaseAsymptote = -45; } else { magAsymptote = 20 * Math.log10(freq/zeroFrequency); phaseAsymptote = 45; } } else { // 超过极点和零点 magAsymptote = 20 * Math.log10(zeroFrequency/poleFrequency); phaseAsymptote = 0; } return { magAsymptote, phaseAsymptote }; } // 绘制伯德图 function drawBodePlots() { // 清除画布 magCtx.clearRect(0, 0, magCanvas.width, magCanvas.height); phaseCtx.clearRect(0, 0, phaseCanvas.width, phaseCanvas.height); // 设置绘图参数 const margin = { top: 30, right: 30, bottom: 50, left: 60 }; const magWidth = magCanvas.width - margin.left - margin.right; const magHeight = magCanvas.height - margin.top - margin.bottom; const phaseWidth = phaseCanvas.width - margin.left - margin.right; const phaseHeight = phaseCanvas.height - margin.top - margin.bottom; // 频率范围 (Hz) - 对数坐标 const minFreq = 10; const maxFreq = 100000; // 绘制幅度图 drawPlot(magCtx, magWidth, magHeight, margin, true); // 绘制相位图 drawPlot(phaseCtx, phaseWidth, phaseHeight, margin, false); } // 绘制单个图表 function drawPlot(ctx, width, height, margin, isMagnitude) { // 设置坐标轴范围 const minFreq = 10; const maxFreq = 100000; const minMag = -40; const maxMag = 20; const minPhase = -90; const maxPhase = 90; // 绘制坐标轴 ctx.strokeStyle = '#34495e'; ctx.lineWidth = 1; // 绘制X轴 ctx.beginPath(); ctx.moveTo(margin.left, margin.top + height); ctx.lineTo(margin.left + width, margin.top + height); ctx.stroke(); // 绘制Y轴 ctx.beginPath(); ctx.moveTo(margin.left, margin.top); ctx.lineTo(margin.left, margin.top + height); ctx.stroke(); // 绘制网格和刻度 drawGrid(ctx, width, height, margin, minFreq, maxFreq, isMagnitude ? minMag : minPhase, isMagnitude ? maxMag : maxPhase, isMagnitude); // 绘制标题 ctx.fillStyle = '#2c3e50'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('频率 (Hz)', margin.left + width/2, margin.top + height + 35); ctx.save(); ctx.translate(15, margin.top + height/2); ctx.rotate(-Math.PI/2); ctx.textAlign = 'center'; ctx.fillText(isMagnitude ? '幅度 (dB)' : '相位 (度)', 0, 0); ctx.restore(); // 绘制曲线 const step = 0.1; // 对数坐标中的步长 let lastX, lastY; // 绘制实际响应曲线 ctx.beginPath(); for (let logFreq = Math.log10(minFreq); logFreq <= Math.log10(maxFreq); logFreq += step) { const freq = Math.pow(10, logFreq); const response = calculateResponse(freq); const value = isMagnitude ? response.magnitude : response.phase; const x = margin.left + (logFreq - Math.log10(minFreq)) / (Math.log10(maxFreq) - Math.log10(minFreq)) * width; const y = margin.top + height - (value - (isMagnitude ? minMag : minPhase)) / ((isMagnitude ? maxMag : maxPhase) - (isMagnitude ? minMag : minPhase)) * height; if (lastX) { ctx.lineTo(x, y); } else { ctx.moveTo(x, y); } lastX = x; lastY = y; } ctx.strokeStyle = '#3498db'; ctx.lineWidth = 2; ctx.stroke(); // 绘制渐近线 ctx.beginPath(); lastX = null; lastY = null; for (let logFreq = Math.log10(minFreq); logFreq <= Math.log10(maxFreq); logFreq += step) { const freq = Math.pow(10, logFreq); const asymptotes = calculateAsymptotes(freq); const value = isMagnitude ? asymptotes.magAsymptote : asymptotes.phaseAsymptote; const x = margin.left + (logFreq - Math.log10(minFreq)) / (Math.log10(maxFreq) - Math.log10(minFreq)) * width; const y = margin.top + height - (value - (isMagnitude ? minMag : minPhase)) / ((isMagnitude ? maxMag : maxPhase) - (isMagnitude ? minMag : minPhase)) * height; if (lastX) { ctx.lineTo(x, y); } else { ctx.moveTo(x, y); } lastX = x; lastY = y; } ctx.strokeStyle = '#e74c3c'; ctx.lineWidth = 1.5; ctx.setLineDash([5, 3]); ctx.stroke(); ctx.setLineDash([]); // 标记截止频率点 const poleX = margin.left + (Math.log10(poleFrequency) - Math.log10(minFreq)) / (Math.log10(maxFreq) - Math.log10(minFreq)) * width; const poleY = margin.top + height - (isMagnitude ? -3 - minMag : -45 - minPhase) / ((isMagnitude ? maxMag : maxPhase) - (isMagnitude ? minMag : minPhase)) * height; ctx.fillStyle = '#27ae60'; ctx.beginPath(); ctx.arc(poleX, poleY, 6, 0, Math.PI * 2); ctx.fill(); // 标记零点 if (zeroFrequency > poleFrequency) { const zeroX = margin.left + (Math.log10(zeroFrequency) - Math.log10(minFreq)) / (Math.log10(maxFreq) - Math.log10(minFreq)) * width; const zeroY = margin.top + height - (isMagnitude ? 0 - minMag : 0 - minPhase) / ((isMagnitude ? maxMag : maxPhase) - (isMagnitude ? minMag : minPhase)) * height; ctx.fillStyle = '#9b59b6'; ctx.beginPath(); ctx.arc(zeroX, zeroY, 6, 0, Math.PI * 2); ctx.fill(); } // 添加标注 ctx.fillStyle = '#2c3e50'; ctx.font = '12px Arial'; ctx.fillText(`极点: ${poleFrequency}Hz`, poleX + 10, poleY - 10); if (zeroFrequency > poleFrequency) { ctx.fillText(`零点: ${zeroFrequency}Hz`, poleX + 10, poleY + 20); } } // 绘制网格和刻度 function drawGrid(ctx, width, height, margin, minFreq, maxFreq, minValue, maxValue, isMagnitude) { ctx.strokeStyle = '#ecf0f1'; ctx.lineWidth = 1; ctx.font = '12px Arial'; ctx.fillStyle = '#7f8c8d'; ctx.textAlign = 'center'; // 水平网格线 (Y轴) for (let value = minValue; value <= maxValue; value += isMagnitude ? 10 : 30) { const y = margin.top + height - (value - minValue) / (maxValue - minValue) * height; ctx.beginPath(); ctx.moveTo(margin.left, y); ctx.lineTo(margin.left + width, y); ctx.stroke(); ctx.fillText(value.toString(), margin.left - 20, y + 4); } // 垂直网格线 (X轴) - 对数刻度 const logMin = Math.log10(minFreq); const logMax = Math.log10(maxFreq); const decades = Math.floor(logMax) - Math.floor(logMin); for (let decade = Math.floor(logMin); decade <= Math.ceil(logMax); decade++) { for (let multiplier = 1; multiplier <= 9; multiplier++) { const freq = multiplier * Math.pow(10, decade); if (freq < minFreq || freq > maxFreq) continue; const logFreq = Math.log10(freq); const x = margin.left + (logFreq - logMin) / (logMax - logMin) * width; if (multiplier === 1) { // 主刻度 (10^n) ctx.beginPath(); ctx.moveTo(x, margin.top); ctx.lineTo(x, margin.top + height); ctx.stroke(); ctx.fillText(formatFrequency(freq), x, margin.top + height + 20); } else { // 次刻度 ctx.beginPath(); ctx.moveTo(x, margin.top + height - 5); ctx.lineTo(x, margin.top + height); ctx.stroke(); } } } } // 格式化频率显示 function formatFrequency(freq) { if (freq >= 1000) { return (freq/1000).toFixed(freq % 1000 === 0 ? 0 : 1) + 'k'; } return freq.toString(); } // 复数表示辅助函数 function 1j() { return { real: 0, imag: 1 }; } // 初始绘制 drawBodePlots(); </script> </body> </html> 上述代码没有任何图像显示
最新发布
07-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值