No.3 元素渲染

了解React元素作为应用构建的基石,掌握如何将元素渲染至DOM,以及React如何高效更新界面。

元素渲染

元素是构成 React 应用的最小单位。

元素用来描述你在屏幕上看到的内容:

const element = <h1>Hello, world</h1>;

与浏览器的 DOM 元素不同,React 当中的元素事实上是普通的对象,React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致。

注意:

初学者很可能把元素的定义和一个内涵更广的定义“组件”给搞混了。我们将会在下一个章节当中对组件进行详细的介绍。元素事实上只是构成组件的一个部分,所以我们还是推荐你先读完这一章节的内容再往后看。

 

将元素渲染到 DOM 中

首先我们在一个 HTML 页面中添加一个 id="root"<div>:

<div id="root"></div>

在此 div 中的所有内容都将由 React DOM 来管理,所以我们将其称之为 “根” DOM 节点。

我们用React 开发应用时一般只会定义一个根节点。但如果你是在一个已有的项目当中引入 React 的话,你可能会需要在不同的部分单独定义 React 根节点。

要将React元素渲染到根DOM节点中,我们通过把它们都传递给 ReactDOM.render() 的方法来将其渲染到页面上:

 

const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));

 

在 CodePen 上试试。

页面上会展示出 “Hello World” 字样。

 

更新元素渲染

React 元素都是immutable 不可变的。当元素被创建之后,你是无法改变其内容或属性的。一个元素就好像是动画里的一帧,它代表应用界面在某一时间点的样子。

根据我们现阶段了解的有关 React 知识,更新界面的唯一办法是创建一个新的元素,然后将它传入 ReactDOM.render() 方法:

来看一下这个计时器的例子:

 

function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(element, document.getElementById('root'));
}

setInterval(tick, 1000);

 

在 CodePen 上试试。

这个示例通过 setInterval() 方法,每秒钟调用一次 ReactDOM.render().

注意:

在实际生产开发中,大多数React应用只会调用一次 ReactDOM.render() 。在下一个章节中我们将会详细介绍 有状态组件 实现 DOM 更新方式。

前后的内容是相辅相成的,所以你最好还是先读完本章节再往下看。

 

React 只会更新必要的部分

React DOM 首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分。

你可以使用浏览器的开发者工具来检查一下之前的例子

DOM inspector showing granular updates

即便我们每秒都创建了一个描述整个UI树的新元素,React DOM 也只会更新渲染文本节点中发生变化的内容。

根据我们以往的经验,将界面视为一个个特定时刻的固定内容(就像一帧一帧的动画),而不是随时处于变化之中(而不是处于变化中的一整段动画),将会有利于我们理清开发思路,减少各种bug。

<think>错误信息指出:`AttributeError: module &#39;matplotlib.pyplot&#39; has no attribute &#39;boxstyles&#39;` 在代码中,我们试图使用`plt.boxstyles.Round`,但正确的访问方式应该是通过`matplotlib.patches`模块中的`BoxStyle`类。 修正步骤: 1. 在代码开头导入`BoxStyle`:`from matplotlib.patches import BoxStyle` 2. 将`boxstyle=plt.boxstyles.Round(pad=0.2)`改为`boxstyle=BoxStyle.Round(pad=0.2)` 另外,在创建图例背景矩形时,我们使用了`plt.boxstyles`,但实际上应该使用`BoxStyle`。同时注意,我们已经在前面导入了`Rectangle`,但没有导入`BoxStyle`。 此外,代码中还有一处使用了`plt.boxstyles.Round`,需要同样修改。 但是,在创建矩形背景时,我们使用的是`plt.Rectangle`,其`boxstyle`参数应该传递一个字符串或者`BoxStyle`对象。我们可以直接使用字符串`"round,pad=0.2"`,或者使用`BoxStyle.Round(pad=0.2)`。 为了简化,这里我们使用字符串的方式,因为这样更简洁,而且之前代码中也是用字符串(但之前写错了模块)。实际上,在创建`Rectangle`时,`boxstyle`参数可以是一个字符串,例如`"round"`,然后通过`pad`等参数调整。 然而,注意我们创建图例背景矩形使用的是`plt.Rectangle`,它并没有`boxstyle`参数!查看matplotlib文档,`Rectangle`并不支持`boxstyle`,`boxstyle`是用于`FancyBboxPatch`的。所以,我们需要使用`FancyBboxPatch`来创建圆角矩形。 因此,我们需要: 1. 导入`FancyBboxPatch`:`from matplotlib.patches import FancyBboxPatch` 2. 使用`FancyBboxPatch`来创建圆角矩形背景。 修正后的图例背景创建代码: 原代码(错误): ```python fig.patches.extend([plt.Rectangle( (legend_x-0.01, legend_y - len(surface_info)*line_height - 0.01), 0.25, len(surface_info)*line_height + 0.02, fill=True, color=&#39;white&#39;, alpha=0.7, transform=fig.transFigure, zorder=100, boxstyle=plt.boxstyles.Round(pad=0.2) # 这里出错 )]) ``` 应改为: ```python from matplotlib.patches import FancyBboxPatch # 创建圆角矩形背景 fig.patches.append(FancyBboxPatch( (legend_x-0.01, legend_y - len(surface_info)*line_height - 0.01), 0.25, len(surface_info)*line_height + 0.02, boxstyle="round,pad=0.2", fill=True, color=&#39;white&#39;, alpha=0.7, transform=fig.transFigure, zorder=100 )) ``` 注意:`FancyBboxPatch`的前两个参数是左下角的xy坐标,然后是宽度和高度。 同时,我们之前已经导入了`matplotlib.patches`,所以只需从其中导入`FancyBboxPatch`。 让我们修改代码: 1. 在文件开头添加导入:`from matplotlib.patches import FancyBboxPatch` 2. 替换背景矩形的创建方式。 另外,在添加彩色方块时,我们使用的是`plt.Rectangle`,这里不需要圆角,所以保持不变。 修改后重新运行。 此外,我们还需要注意,图例背景矩形的位置和大小可能需要微调,但这里先按原位置。 下面给出完整的修正代码: </think>我发现了问题所在。错误是 `AttributeError: module &#39;matplotlib.pyplot&#39; has no attribute &#39;boxstyles&#39;`,这是因为 `boxstyles` 应该从 `matplotlib.patches` 导入,而不是从 `pyplot`。以下是修正后的完整代码: ```python import pandas as pd import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata from matplotlib.colors import LinearSegmentedColormap, Normalize import os from matplotlib.patches import FancyBboxPatch, Rectangle # 修正导入 import matplotlib.patches as mpatches # 添加正确导入 def generate_multi_fsimc_surfaces(): # 1. 从外部Excel文件加载数据 print("从Excel加载原始数据...") try: # 获取当前脚本所在目录 script_dir = os.path.dirname(os.path.abspath(__file__)) # 构建Excel文件完整路径 excel_path = os.path.join(script_dir, &#39;fsim_data.xlsx&#39;) # 读取Excel数据 df = pd.read_excel(excel_path) # 校验数据格式 required_columns = [&#39;BOW&#39;, &#39;WAVE&#39;] if not all(col in df.columns for col in required_columns): missing = [col for col in required_columns if col not in df.columns] raise ValueError(f"Excel文件缺少必要的列: {missing}") # 识别所有FsimC列 fsimc_columns = [col for col in df.columns if col.startswith(&#39;FsimC&#39;)] if not fsimc_columns: raise ValueError("未找到任何以&#39;FsimC&#39;开头的列") print(f"找到 {len(fsimc_columns)} 个FsimC列: {&#39;, &#39;.join(fsimc_columns)}") points = df[[&#39;BOW&#39;, &#39;WAVE&#39;]].values fsimc_values = {col: df[col].values for col in fsimc_columns} # 计算所有FsimC值的全局范围 all_fsimc_min = float(&#39;inf&#39;) all_fsimc_max = float(&#39;-inf&#39;) for col in fsimc_columns: col_min = df[col].min() col_max = df[col].max() if col_min < all_fsimc_min: all_fsimc_min = col_min if col_max > all_fsimc_max: all_fsimc_max = col_max # 添加10%的padding以确保所有数据点都在范围内 z_padding = (all_fsimc_max - all_fsimc_min) * 0.1 z_min = all_fsimc_min - z_padding z_max = all_fsimc_max + z_padding print(f"全局FsimC范围: {all_fsimc_min:.4f} 到 {all_fsimc_max:.4f}") print(f"设置Z轴范围: {z_min:.4f} 到 {z_max:.4f}") except FileNotFoundError: print(f"错误: 找不到数据文件 {excel_path}") print("请确保fsim_data.xlsx文件位于脚本同一目录下") return except Exception as e: print(f"加载数据时出错: {str(e)}") return # 2. 创建超高分辨率网格 print("创建高分辨率网格...") x_fine = np.linspace(1, 5, 150) # 弓形变形 (150点) y_fine = np.linspace(0.1, 1.0, 100) # 波形变形 (100点) X_fine, Y_fine = np.meshgrid(x_fine, y_fine) # 3. 创建3D图表 print("创建3D图表...") fig = plt.figure(figsize=(18, 14)) ax = fig.add_subplot(111, projection=&#39;3d&#39;) # 4. 定义曲面颜色和透明度 # 使用不同颜色区分不同曲面 color_maps = [ plt.cm.Blues, # 蓝色系 plt.cm.Greens, # 绿色系 plt.cm.Reds, # 红色系 plt.cm.Purples, # 紫色系 plt.cm.Oranges, # 橙色系 plt.cm.Greys # 灰色系 ] # 透明度设置 (第一个曲面最不透明,后续逐渐透明) alphas = [0.65, 0.55, 0.45, 0.35, 0.25, 0.15] # 存储每个曲面的颜色映射和标签颜色 surface_info = [] # 5. 为每个FsimC列创建曲面 print(f"开始处理 {len(fsimc_columns)} 个曲面...") for i, (col_name, values) in enumerate(fsimc_values.items()): print(f"正在处理列: {col_name} ({i+1}/{len(fsimc_columns)})") # 插值数据 print(" 执行插值...") Z_fine = griddata(points, values, (X_fine, Y_fine), method=&#39;cubic&#39;) # 计算网格中点值 Z_top_left = Z_fine[:-1, :-1] Z_top_right = Z_fine[:-1, 1:] Z_bottom_left = Z_fine[1:, :-1] Z_bottom_right = Z_fine[1:, 1:] Z_midpoints = (Z_top_left + Z_top_right + Z_bottom_left + Z_bottom_right) / 4.0 # 选择颜色映射和透明度 cmap_idx = i % len(color_maps) alpha = alphas[min(i, len(alphas)-1)] # 创建归一化对象 - 使用曲面特定的范围 vmin = np.nanmin(Z_midpoints) vmax = np.nanmax(Z_midpoints) norm = Normalize(vmin=vmin, vmax=vmax) # 创建渐变颜色映射 gradient_colors = color_maps[cmap_idx](np.linspace(0.2, 0.8, 256)) gradient_colors[:, 3] = alpha # 设置透明度 cmap_with_alpha = LinearSegmentedColormap.from_list(f"{col_name}_cmap", gradient_colors, N=2048) # 应用渐变颜色映射 facecolors = cmap_with_alpha(norm(Z_midpoints)) # 绘制曲面 print(f" 渲染曲面 (透明度: {alpha:.2f})...") stride_x = 1 stride_y = 1 if len(x_fine) > 100: render_density = max(1, int(len(x_fine)/75)) stride_x = render_density stride_y = max(1, int(render_density/1.5)) # 绘制曲面,使用zorder确保顺序正确 surf = ax.plot_surface( X_fine[::stride_y, ::stride_x], Y_fine[::stride_y, ::stride_x], Z_fine[::stride_y, ::stride_x], facecolors=facecolors[::stride_y, ::stride_x], shade=False, rcount=min(300, len(y_fine)), ccount=min(300, len(x_fine)), alpha=alpha, antialiased=True, edgecolor=&#39;black&#39;, linewidth=0.1, zorder=len(fsimc_columns)-i # 确保后面的曲面在顶部 ) # 存储曲面信息用于图例 label_color = gradient_colors[int(0.8 * 256)] # 使用较深颜色作为标签 surface_info.append({ &#39;col_name&#39;: col_name, &#39;color&#39;: label_color, &#39;cmap&#39;: cmap_with_alpha, &#39;min&#39;: vmin, &#39;max&#39;: vmax }) # 为曲面添加标签 ax.text(5.5, 1.1, np.nanmax(Z_fine)+0.005, col_name, color=label_color[:3], # 使用颜色映射中较深的颜色 fontsize=10, fontweight=&#39;bold&#39;, zorder=len(fsimc_columns)+1) # 确保标签在最上层 # 6. 添加公共颜色条 print("添加公共颜色条...") sm = plt.cm.ScalarMappable(cmap=plt.cm.viridis) # 使用通用颜色映射 sm.set_array([]) # 空数组用于颜色条 cbar = fig.colorbar(sm, ax=ax, shrink=0.7, aspect=20, pad=0.12) cbar.set_label(&#39;FsimC Range&#39;, fontsize=14, labelpad=20) cbar.ax.tick_params(labelsize=11) # 7. 设置坐标轴和网格线 ax.set_xlabel(&#39;BOW (‰)&#39;, fontsize=15, labelpad=20) ax.set_ylabel(&#39;WAVE (‰)&#39;, fontsize=15, labelpad=20) ax.set_zlabel(&#39;FsimC&#39;, fontsize=15, labelpad=20) ax.set_title(f&#39;Multi-FsimC Analysis ({len(fsimc_columns)} Surfaces)&#39;, fontsize=20, pad=35) # 设置刻度 ax.set_xticks(np.linspace(1, 5, 5)) # 设置Y轴刻度为0.05的倍数 y_ticks = np.arange(0.1, 1.05, 0.05) # 从0.1到1.0,步长0.05 ax.set_yticks(y_ticks) # 格式化小数点位数 ax.set_yticklabels([f"{y:.2f}" if y % 0.1 == 0 else f"{y:.3f}" for y in y_ticks]) # 动态设置Z轴刻度 z_ticks = np.linspace(z_min, z_max, 5) ax.set_zticks(z_ticks) # 设置刻度标签大小 ax.tick_params(axis=&#39;both&#39;, which=&#39;major&#39;, labelsize=11, pad=8) # 添加背景网格线 print("添加背景网格线...") ax.grid(True, linestyle=&#39;-&#39;, linewidth=0.7, alpha=0.35) ax.xaxis._axinfo[&#39;grid&#39;].update({&#39;linewidth&#39;: 0.8, &#39;alpha&#39;: 0.45, &#39;color&#39;: &#39;darkgray&#39;}) ax.yaxis._axinfo[&#39;grid&#39;].update({&#39;linewidth&#39;: 0.8, &#39;alpha&#39;: 0.45, &#39;color&#39;: &#39;darkgray&#39;}) ax.zaxis._axinfo[&#39;grid&#39;].update({&#39;linewidth&#39;: 0.8, &#39;alpha&#39;: 0.45, &#39;color&#39;: &#39;darkgray&#39;}) # 设置坐标平面样式 ax.xaxis.pane.fill = True ax.yaxis.pane.fill = True ax.zaxis.pane.fill = True ax.xaxis.pane.set_alpha(0.05) ax.yaxis.pane.set_alpha(0.05) ax.zaxis.pane.set_alpha(0.05) ax.xaxis.pane.set_edgecolor(&#39;lightgray&#39;) ax.yaxis.pane.set_edgecolor(&#39;lightgray&#39;) ax.zaxis.pane.set_edgecolor(&#39;lightgray&#39;) # 8. 配置轴测图投影 print("配置轴测图投影...") ax.set_proj_type(&#39;ortho&#39;) # 正交投影 ax.view_init(elev=30, azim=45) ax.set_box_aspect(aspect=(1, 1, 1)) # 调整坐标轴范围 ax.set_xlim(0.5, 5.5) ax.set_ylim(0.0, 1.1) ax.set_zlim(z_min, z_max) # 使用动态计算的Z轴范围 # 9. 添加彩色图例说明 print("添加彩色图例...") legend_x = 0.05 legend_y = 0.95 line_height = 0.025 # 创建图例背景 - 使用FancyBboxPatch代替Rectangle fig.patches.append(FancyBboxPatch( (legend_x-0.01, legend_y - len(surface_info)*line_height - 0.01), 0.25, len(surface_info)*line_height + 0.02, boxstyle="round,pad=0.2", # 使用字符串指定boxstyle fill=True, color=&#39;white&#39;, alpha=0.7, transform=fig.transFigure, zorder=100 )) # 添加图例标题 fig.text(legend_x, legend_y, "FsimC Columns:", fontsize=12, transform=fig.transFigure, fontweight=&#39;bold&#39;, zorder=101) # 添加彩色图例项 for i, info in enumerate(surface_info): y_pos = legend_y - (i+1)*line_height # 添加颜色方块 fig.patches.append(mpatches.Rectangle( # 使用mpatches.Rectangle (legend_x, y_pos - line_height/2), 0.015, 0.015, facecolor=info[&#39;color&#39;], transform=fig.transFigure, zorder=102 )) # 添加带颜色的文本 fig.text(legend_x + 0.02, y_pos, f"{i+1}. {info[&#39;col_name&#39;]}", transform=fig.transFigure, fontsize=11, color=info[&#39;color&#39;][:3], # RGB颜色,忽略透明度 zorder=103) # 10. 保存图像 print("保存多曲面轴测图...") plt.savefig(f&#39;multi_fsimc_surfaces_{len(fsimc_columns)}.png&#39;, dpi=350, bbox_inches=&#39;tight&#39;) # 11. 显示图表 print("渲染完成,显示图表...") plt.tight_layout() plt.show() if __name__ == "__main__": generate_multi_fsimc_surfaces() ``` ### 修正内容说明: 1. **导入修正**: ```python from matplotlib.patches import FancyBboxPatch, Rectangle import matplotlib.patches as mpatches # 添加正确导入 ``` 2. **图例背景创建方式修正**: - 使用 `FancyBboxPatch` 替代 `Rectangle` - 使用字符串指定 `boxstyle` 参数: ```python fig.patches.append(FancyBboxPatch( (legend_x-0.01, legend_y - len(surface_info)*line_height - 0.01), 0.25, len(surface_info)*line_height + 0.02, boxstyle="round,pad=0.2", # 修正后的boxstyle指定方式 fill=True, color=&#39;white&#39;, alpha=0.7, transform=fig.transFigure, zorder=100 )) ``` 3. **颜色方块创建方式修正**: - 使用 `mpatches.Rectangle` 替代 `plt.Rectangle`: ```python fig.patches.append(mpatches.Rectangle( (legend_x, y_pos - line_height/2), 0.015, 0.015, facecolor=info[&#39;color&#39;], transform=fig.transFigure, zorder=102 )) ``` ### 为什么会出现这个错误? 在原始代码中,我们试图访问 `plt.boxstyles`,但 Matplotlib 的 `boxstyles` 实际上位于 `matplotlib.patches` 模块中,而不是在 `pyplot` 中。Pyplot (`plt`) 主要用于创建图表和坐标轴,而具体的图形元素(如矩形、圆形等)通常在 `matplotlib.patches` 中定义。 ### 修正后的代码优势: - 完全兼容 Matplotlib 的正确导入方式 - 保留了所有原有功能,包括曲面渐变着色和Y轴0.05倍数刻度 - 使用更专业的 `FancyBboxPatch` 创建圆角矩形背景 - 确保代码在各种 Python 环境中都能正常运行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值