Symfony 如何把批处理数据转数组

处理文件上传时可使用symfony serializer组件或fgetcsv函数将csv数据逐行解析为关联数组;2. 数据库查询结果可通过doctrine orm的getarrayresult()或dbal的fetchallassociative()直接获取数组;3. json数据用json_decode转换,xml可用simplexml或symfony serializer解码为数组;4. 大型文件应使用生成器或doctrine的iterate()实现流式处理,避免内存溢出;5. 数据验证推荐使用symfony validator组件结合约束集合,或编写自定义验证逻辑;6. 错误处理应收集每条记录的错误信息并生成报告,而非中断整个流程;7. 更优雅的模式包括使用dto封装数据、命令总线分发处理任务、以及通过symfony messenger组件异步发送消息到队列。

Symfony 如何把批处理数据转数组

在 Symfony 应用中,将批处理数据转换为数组,本质上是根据数据的原始格式(无论是 CSV、JSON、数据库结果集还是其他形式),利用 PHP 原生功能或 Symfony 相关组件进行迭代和结构化映射。这并非一个单一的 Symfony 功能,而是编程实践的结合,核心在于高效地解析输入并构建出你期望的数组结构。

解决方案

将批处理数据转换为数组,具体做法取决于你的数据来源和规模。以下是几种常见场景及我的处理思路:

1. 处理文件上传(CSV/Excel 等表格数据)

这大概是我最常遇到的场景了。用户上传一个 CSV 文件,里面有成千上万条记录,我需要把它们一条条解析出来,转换成 PHP 数组,然后可能存入数据库或者做其他处理。

使用 Symfony Serializer 组件 (CsvEncoder)如果你的 Symfony 项目已经引入了

symfony/serializer

组件,这是一个非常优雅且强大的方式。它能处理多种格式,包括 CSV。

use SymfonyComponentSerializerEncoderCsvEncoder;use SymfonyComponentSerializerNormalizerArrayDenormalizer;use SymfonyComponentSerializerSerializer;use SymfonyComponentHttpFoundationFileFile;// 假设你已经获取了上传的 CSV 文件路径$filePath = '/path/to/your/uploaded/data.csv';$fileContent = file_get_contents($filePath);$encoders = [new CsvEncoder()];$normalizers = [new ArrayDenormalizer()]; // 如果需要将嵌套数据也转为数组$serializer = new Serializer($normalizers, $encoders);// CSV 数据通常没有明确的根元素,直接解码即可// context 参数可以设置 delimiter, enclosure, escape_char 等$data = $serializer->decode($fileContent, 'csv', [    CsvEncoder::DELIMITER_KEY => ',',    CsvEncoder::ENCLOSURE_KEY => '"',    CsvEncoder::ESCAPE_CHAR_KEY => '',    // 如果CSV文件有表头,可以这样设置,否则会把第一行当数据    CsvEncoder::AS_COLLECTION_KEY => true, // 确保返回的是一个数组的数组]);// $data 现在就是一个包含多个记录数组的数组,例如:// [//     ['header1' => 'value1', 'header2' => 'valueA'],//     ['header1' => 'value2', 'header2' => 'valueB'],// ]

使用 PHP 原生

fgetcsv

函数对于简单的 CSV 文件,或者你不想引入太多依赖,

fgetcsv

是个非常直接的选择。它逐行读取,内存占用小,适合大文件。

$filePath = '/path/to/your/uploaded/data.csv';$batchData = [];$header = [];$isFirstRow = true;if (($handle = fopen($filePath, 'r')) !== false) {    while (($row = fgetcsv($handle, 1000, ',')) !== false) {        if ($isFirstRow) {            // 假设第一行是表头            $header = $row;            $isFirstRow = false;            continue;        }        // 将行数据与表头关联起来,形成关联数组        if (count($header) === count($row)) {            $batchData[] = array_combine($header, $row);        } else {            // 处理行数据与表头数量不匹配的情况,比如记录日志            // error_log("CSV row has mismatched columns: " . implode(',', $row));        }    }    fclose($handle);}// $batchData 现在就是你想要的数组集合

2. 处理数据库查询结果

如果你从数据库中查询出大量数据需要进行批处理,Doctrine ORM 或 DBAL 提供了直接获取数组结果的方法。

Doctrine ORM:当你使用 DQL (Doctrine Query Language) 或者 Query Builder 时,可以指定结果格式为数组。

// 在你的 Repository 或 Service 中$entityManager = $this->getDoctrine()->getManager();$repository = $entityManager->getRepository(YourEntity::class);// 方式一:使用 QueryBuilder$queryBuilder = $repository->createQueryBuilder('e')    ->select('e.id', 'e.name', 'e.status')    ->where('e.status = :status')    ->setParameter('status', 'active');$results = $queryBuilder->getQuery()->getArrayResult();// $results 就是一个包含实体数据数组的数组// 例如:[['id' => 1, 'name' => 'Item A', 'status' => 'active'], ...]

Doctrine DBAL (直接 SQL)对于更复杂的查询或者性能敏感的场景,直接使用 DBAL 更加灵活。

$connection = $this->getDoctrine()->getConnection(); // 获取数据库连接$sql = 'SELECT id, name, status FROM your_table WHERE status = :status';$stmt = $connection->prepare($sql);$resultSet = $stmt->executeQuery(['status' => 'active']);$batchData = $resultSet->fetchAllAssociative();// $batchData 也是一个包含关联数组的数组

3. 处理 API 接口的 JSON/XML 批量数据

如果你的批处理数据来源于外部 API,通常是 JSON 或 XML 格式。

JSON 数据:PHP 原生的

json_decode

函数非常高效。

$jsonString = file_get_contents('php://input'); // 假设是 POST 请求体// 或者 $jsonString = $response->getContent(); // 来自 HTTP 客户端的响应$batchData = json_decode($jsonString, true); // true 表示解码为关联数组// 记得处理 json_last_error() 检查解码是否成功if (json_last_error() !== JSON_ERROR_NONE) {    // 错误处理}

XML 数据:可以使用

simplexml_load_string

simplexml_load_file

,然后遍历

SimpleXMLElement

对象。如果结构复杂,

symfony/serializer

同样能派上用场。

use SymfonyComponentSerializerEncoderXmlEncoder;use SymfonyComponentSerializerNormalizerArrayDenormalizer;use SymfonyComponentSerializerSerializer;$xmlString = '1Product A2Product B';$encoders = [new XmlEncoder()];$normalizers = [new ArrayDenormalizer()];$serializer = new Serializer($normalizers, $encoders);$batchData = $serializer->decode($xmlString, 'xml');// $batchData 可能是 ['item' => [['id' => '1', 'name' => 'Product A'], ['id' => '2', 'name' => 'Product B']]]// 或者根据你的XML结构,可能需要进一步处理

处理大型批处理文件时,如何避免内存溢出?

处理动辄上万、上百万条记录的批处理数据时,最怕的就是内存爆炸。我以前就踩过这种坑,尤其是在把整个文件内容一次性

file_get_contents

到内存,或者 Doctrine 一次性

getResult()

几万条记录的时候。避免内存溢出的关键在于“流式处理”和“分块处理”,而不是一次性加载所有数据。

使用 PHP Generator(生成器)这是 PHP 5.5+ 提供的一个非常棒的特性,它允许你编写一个函数,在每次需要数据时才生成并返回一个值,而不是一次性返回一个完整的数组。这对于处理大型数据集非常有效,因为数据不会全部加载到内存中。

// 示例:使用生成器读取大型 CSV 文件function readCsvRows(string $filePath): Generator{    if (($handle = fopen($filePath, 'r')) !== false) {        $header = fgetcsv($handle); // 读取表头        while (($row = fgetcsv($handle)) !== false) {            if ($header && count($header) === count($row)) {                yield array_combine($header, $row); // 每次返回一行数据            } else {                // 错误处理或跳过            }        }        fclose($handle);    }}// 使用时:foreach (readCsvRows('/path/to/large_data.csv') as $rowData) {    // 处理 $rowData,每次只处理一行,内存占用极低    // $rowData 已经是数组了}

对于数据库查询,Doctrine ORM 也提供了类似生成器的

iterate()

方法。

Doctrine ORM 的

iterate()

方法当查询结果集非常大时,

iterate()

方法可以帮助你避免内存溢出。它会以迭代器的方式逐个返回实体对象,而不是一次性将所有实体加载到内存中。

$entityManager = $this->getDoctrine()->getManager();$query = $entityManager->getRepository(YourEntity::class)    ->createQueryBuilder('e')    ->where('e.createdAt setParameter('date', new DateTime('-1 year'))    ->getQuery();// 使用 iterate() 方法foreach ($query->iterate() as $row) {    $entity = $row[0]; // iterate() 返回的是一个数组,实体在第一个元素    // 现在你可以处理这个 $entity,例如转换为数组    $entityAsArray = [        'id' => $entity->getId(),        'name' => $entity->getName(),        // ... 其他属性    ];    // 处理 $entityAsArray    $entityManager->detach($entity); // 处理完后立即分离实体,释放内存}$entityManager->clear(); // 批处理结束后清除所有托管实体

分块处理 (Chunking)如果你的数据源不支持流式读取(比如某些 API 限制),或者你需要在内存中积累一定数量的数据再统一处理(例如批量插入数据库),那么分块处理是一个好办法。你可以通过 LIMIT/OFFSET 或者其他分页机制,每次只取一小部分数据进行处理。

$batchSize = 1000;$offset = 0;do {    $queryBuilder = $entityManager->getRepository(YourEntity::class)        ->createQueryBuilder('e')        ->orderBy('e.id', 'ASC')        ->setFirstResult($offset)        ->setMaxResults($batchSize);    $entities = $queryBuilder->getQuery()->getResult(); // 获取一批实体    if (empty($entities)) {        break; // 没有更多数据了    }    foreach ($entities as $entity) {        // 将实体转换为数组并处理        $entityAsArray = [            'id' => $entity->getId(),            'name' => $entity->getName(),        ];        // ... 处理 $entityAsArray    }    $entityManager->clear(); // 清理当前批次的实体    $offset += $batchSize;} while (true);

转换过程中如何处理数据验证和错误?

批处理数据往往是“脏”的,会有各种格式不符、数据缺失或逻辑错误。一个健壮的批处理流程必须包含完善的验证和错误处理机制。我个人认为,这块儿比数据转换本身更重要。

使用 Symfony Validator 组件这是 Symfony 推荐的验证方式,你可以定义实体的约束,然后对转换后的数组数据进行验证。

use SymfonyComponentValidatorValidatorValidatorInterface;use SymfonyComponentValidatorConstraints as Assert;// 假设你有一个服务注入了 ValidatorInterfaceclass BatchProcessor{    private $validator;    public function __construct(ValidatorInterface $validator)    {        $this->validator = $validator;    }    public function processData(array $rowData): array    {        // 定义你的验证约束,可以针对数组的每个字段        $constraints = new AssertCollection([            'id' => [new AssertNotBlank(), new AssertType('integer')],            'name' => [new AssertNotBlank(), new AssertLength(['min' => 3])],            'email' => [new AssertNotBlank(), new AssertEmail()],        ]);        $violations = $this->validator->validate($rowData, $constraints);        if (count($violations) > 0) {            $errors = [];            foreach ($violations as $violation) {                $errors[] = sprintf('%s: %s', $violation->getPropertyPath(), $violation->getMessage());            }            // 记录错误,或者抛出异常,或者返回错误信息            // 我通常会把这些错误收集起来,最后统一报告            throw new InvalidArgumentException(implode(', ', $errors));        }        return $rowData; // 验证通过,返回数据    }}// 在你的批处理循环中调用try {    $validatedData = $batchProcessor->processData($rowData);    // 数据有效,继续处理} catch (InvalidArgumentException $e) {    // 记录错误日志,将此行标记为失败,或者跳过    error_log('Validation error for row: ' . $e->getMessage());    // 收集错误行,以便后续生成错误报告}

自定义验证逻辑对于一些业务逻辑上的复杂验证,或者不适合用 Validator 组件表达的场景,直接编写 PHP 代码进行判断更灵活。

function isValidProductData(array $productData): bool{    if (!isset($productData['price']) || !is_numeric($productData['price']) || $productData['price'] <= 0) {        return false;    }    if (isset($productData['stock']) && $productData['stock'] < 0) {        return false;    }    // 更多业务规则...    return true;}// 在循环中:if (isValidProductData($rowData)) {    // 有效数据} else {    // 无效数据,记录错误}

错误收集与报告在批处理中,很少因为一条错误记录就中断整个进程。更常见的做法是:

跳过错误记录: 记录错误详情,然后继续处理下一条。收集错误: 将所有无效记录及其错误信息收集起来,最后生成一份详细的错误报告(例如 CSV 文件、邮件通知或日志)。日志记录: 使用 Monolog 等日志工具,详细记录哪些数据行失败了,失败原因是什么。

我通常会维护一个

failedRecords

数组,或者一个专门的日志文件,记录下所有处理失败的行号、原始数据和错误信息,这样方便后续排查和修正。

除了直接转数组,Symfony 中是否有更优雅的数据处理模式?

当然有。直接将批处理数据转为数组只是第一步,后续如何高效、安全、可维护地处理这些数据,才是真正考验架构设计的地方。Symfony 提供了几个组件和模式,可以让你的数据处理流程更加优雅。

数据传输对象 (DTOs – Data Transfer Objects)将原始的数组数据转换为类型明确的 PHP 对象,这能极大地提高代码的可读性和可维护性,并利用 IDE 的类型提示。你可以为每种批处理数据定义一个 DTO 类。

// 定义一个 DTOclass ProductImportDto{    public ?int $id = null;    public string $name;    public float $price;    public int $stock;    public ?string $email = null;    // 可以添加构造函数或其他辅助方法    public static function fromArray(array $data): self    {        $dto = new self();        $dto->id = $data['id'] ?? null;        $dto->name = $data['name'] ?? '';        $dto->price = (float)($data['price'] ?? 0.0);        $dto->stock = (int)($data['stock'] ?? 0);        $dto->email = $data['email'] ?? null;        return $dto;    }}// 在你的批处理循环中:foreach (readCsvRows($filePath) as $rowData) {    try {        $productDto = ProductImportDto::fromArray($rowData);        // 此时 $productDto 是一个强类型对象,可以对其进行验证和后续处理        $violations = $this->validator->validate($productDto); // 验证 DTO        if (count($violations) > 0) {            // 处理验证错误            continue;        }        // 将 DTO 传递给业务服务进行处理        $this->productService->importProduct($productDto);    } catch (Throwable $e) {        // 记录错误    }}

配合 Symfony Serializer 的

ObjectNormalizer

,甚至可以直接将数组反序列化为 DTO 对象,省去手动映射的麻烦。

命令总线 (Command Bus) 模式对于批处理中的每一条记录,你可以将其封装成一个“命令”对象,然后通过命令总线将其分发给对应的“命令处理器”。这让你的业务逻辑变得非常清晰和可测试。

// 定义一个命令class ImportProductCommand{    public ProductImportDto $productDto;    public function __construct(ProductImportDto $productDto)    {        $this->productDto = $productDto;    }}// 定义一个命令处理器class ImportProductCommandHandler implements MessageHandlerInterface // 配合 Symfony Messenger{    private ProductService $productService;    public function __construct(ProductService $productService)    {        $this->productService = $productService;    }    public function __invoke(ImportProductCommand $command)    {        $this->productService->importProduct($command->productDto);    }}// 在你的批处理循环中:foreach ($batchDataAsDtos as $productDto) {    $command = new ImportProductCommand($productDto);    $this->commandBus->dispatch($command); // 将命令发送到总线}

消息队列 (Message Queue) 与 Symfony Messenger 组件对于大型批处理任务,如果你的业务允许异步处理,那么将每条记录(或每批记录)作为一条消息发送到消息队列中,然后由后台的消费者(Consumer)异步处理,是最高效且最具弹性的方式。这能避免请求超时,提高用户体验,并允许你水平扩展处理能力。Symfony 的 Messenger 组件就是为此而生。

use SymfonyComponentMessengerMessageBusInterface;// ... 假设你已经将数据转换为 DTOs// 在你的批处理循环中:

以上就是Symfony 如何把批处理数据转数组的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1267903.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月10日 10:42:13
下一篇 2025年12月10日 10:43:26

相关推荐

  • Uniapp 中如何不拉伸不裁剪地展示图片?

    灵活展示图片:如何不拉伸不裁剪 在界面设计中,常常需要以原尺寸展示用户上传的图片。本文将介绍一种在 uniapp 框架中实现该功能的简单方法。 对于不同尺寸的图片,可以采用以下处理方式: 极端宽高比:撑满屏幕宽度或高度,再等比缩放居中。非极端宽高比:居中显示,若能撑满则撑满。 然而,如果需要不拉伸不…

    2025年12月24日
    400
  • 如何让小说网站控制台显示乱码,同时网页内容正常显示?

    如何在不影响用户界面的情况下实现控制台乱码? 当在小说网站上下载小说时,大家可能会遇到一个问题:网站上的文本在网页内正常显示,但是在控制台中却是乱码。如何实现此类操作,从而在不影响用户界面(UI)的情况下保持控制台乱码呢? 答案在于使用自定义字体。网站可以通过在服务器端配置自定义字体,并通过在客户端…

    2025年12月24日
    800
  • SASS 中的 Mixins

    mixin 是 css 预处理器提供的工具,虽然它们不是可以被理解的函数,但它们的主要用途是重用代码。 不止一次,我们需要创建多个类来执行相同的操作,但更改单个值,例如字体大小的多个类。 .fs-10 { font-size: 10px;}.fs-20 { font-size: 20px;}.fs-…

    2025年12月24日
    000
  • 如何在地图上轻松创建气泡信息框?

    地图上气泡信息框的巧妙生成 地图上气泡信息框是一种常用的交互功能,它简便易用,能够为用户提供额外信息。本文将探讨如何借助地图库的功能轻松创建这一功能。 利用地图库的原生功能 大多数地图库,如高德地图,都提供了现成的信息窗体和右键菜单功能。这些功能可以通过以下途径实现: 高德地图 JS API 参考文…

    2025年12月24日
    400
  • 如何使用 scroll-behavior 属性实现元素scrollLeft变化时的平滑动画?

    如何实现元素scrollleft变化时的平滑动画效果? 在许多网页应用中,滚动容器的水平滚动条(scrollleft)需要频繁使用。为了让滚动动作更加自然,你希望给scrollleft的变化添加动画效果。 解决方案:scroll-behavior 属性 要实现scrollleft变化时的平滑动画效果…

    2025年12月24日
    000
  • 如何为滚动元素添加平滑过渡,使滚动条滑动时更自然流畅?

    给滚动元素平滑过渡 如何在滚动条属性(scrollleft)发生改变时为元素添加平滑的过渡效果? 解决方案:scroll-behavior 属性 为滚动容器设置 scroll-behavior 属性可以实现平滑滚动。 html 代码: click the button to slide right!…

    2025年12月24日
    500
  • 如何选择元素个数不固定的指定类名子元素?

    灵活选择元素个数不固定的指定类名子元素 在网页布局中,有时需要选择特定类名的子元素,但这些元素的数量并不固定。例如,下面这段 html 代码中,activebar 和 item 元素的数量均不固定: *n *n 如果需要选择第一个 item元素,可以使用 css 选择器 :nth-child()。该…

    2025年12月24日
    200
  • 使用 SVG 如何实现自定义宽度、间距和半径的虚线边框?

    使用 svg 实现自定义虚线边框 如何实现一个具有自定义宽度、间距和半径的虚线边框是一个常见的前端开发问题。传统的解决方案通常涉及使用 border-image 引入切片图片,但是这种方法存在引入外部资源、性能低下的缺点。 为了避免上述问题,可以使用 svg(可缩放矢量图形)来创建纯代码实现。一种方…

    2025年12月24日
    100
  • 如何让“元素跟随文本高度,而不是撑高父容器?

    如何让 元素跟随文本高度,而不是撑高父容器 在页面布局中,经常遇到父容器高度被子元素撑开的问题。在图例所示的案例中,父容器被较高的图片撑开,而文本的高度没有被考虑。本问答将提供纯css解决方案,让图片跟随文本高度,确保父容器的高度不会被图片影响。 解决方法 为了解决这个问题,需要将图片从文档流中脱离…

    2025年12月24日
    000
  • 为什么 CSS mask 属性未请求指定图片?

    解决 css mask 属性未请求图片的问题 在使用 css mask 属性时,指定了图片地址,但网络面板显示未请求获取该图片,这可能是由于浏览器兼容性问题造成的。 问题 如下代码所示: 立即学习“前端免费学习笔记(深入)”; icon [data-icon=”cloud”] { –icon-cl…

    2025年12月24日
    200
  • 如何利用 CSS 选中激活标签并影响相邻元素的样式?

    如何利用 css 选中激活标签并影响相邻元素? 为了实现激活标签影响相邻元素的样式需求,可以通过 :has 选择器来实现。以下是如何具体操作: 对于激活标签相邻后的元素,可以在 css 中使用以下代码进行设置: li:has(+li.active) { border-radius: 0 0 10px…

    2025年12月24日
    100
  • 如何模拟Windows 10 设置界面中的鼠标悬浮放大效果?

    win10设置界面的鼠标移动显示周边的样式(探照灯效果)的实现方式 在windows设置界面的鼠标悬浮效果中,光标周围会显示一个放大区域。在前端开发中,可以通过多种方式实现类似的效果。 使用css 使用css的transform和box-shadow属性。通过将transform: scale(1.…

    2025年12月24日
    200
  • 为什么我的 Safari 自定义样式表在百度页面上失效了?

    为什么在 Safari 中自定义样式表未能正常工作? 在 Safari 的偏好设置中设置自定义样式表后,您对其进行测试却发现效果不同。在您自己的网页中,样式有效,而在百度页面中却失效。 造成这种情况的原因是,第一个访问的项目使用了文件协议,可以访问本地目录中的图片文件。而第二个访问的百度使用了 ht…

    2025年12月24日
    000
  • 如何用前端实现 Windows 10 设置界面的鼠标移动探照灯效果?

    如何在前端实现 Windows 10 设置界面中的鼠标移动探照灯效果 想要在前端开发中实现 Windows 10 设置界面中类似的鼠标移动探照灯效果,可以通过以下途径: CSS 解决方案 DEMO 1: Windows 10 网格悬停效果:https://codepen.io/tr4553r7/pe…

    2025年12月24日
    000
  • 使用CSS mask属性指定图片URL时,为什么浏览器无法加载图片?

    css mask属性未能加载图片的解决方法 使用css mask属性指定图片url时,如示例中所示: mask: url(“https://api.iconify.design/mdi:apple-icloud.svg”) center / contain no-repeat; 但是,在网络面板中却…

    2025年12月24日
    000
  • 如何用CSS Paint API为网页元素添加时尚的斑马线边框?

    为元素添加时尚的斑马线边框 在网页设计中,有时我们需要添加时尚的边框来提升元素的视觉效果。其中,斑马线边框是一种既醒目又别致的设计元素。 实现斜向斑马线边框 要实现斜向斑马线间隔圆环,我们可以使用css paint api。该api提供了强大的功能,可以让我们在元素上绘制复杂的图形。 立即学习“前端…

    2025年12月24日
    000
  • 图片如何不撑高父容器?

    如何让图片不撑高父容器? 当父容器包含不同高度的子元素时,父容器的高度通常会被最高元素撑开。如果你希望父容器的高度由文本内容撑开,避免图片对其产生影响,可以通过以下 css 解决方法: 绝对定位元素: .child-image { position: absolute; top: 0; left: …

    2025年12月24日
    000
  • CSS 帮助

    我正在尝试将文本附加到棕色框的左侧。我不能。我不知道代码有什么问题。请帮助我。 css .hero { position: relative; bottom: 80px; display: flex; justify-content: left; align-items: start; color:…

    2025年12月24日 好文分享
    200
  • 前端代码辅助工具:如何选择最可靠的AI工具?

    前端代码辅助工具:可靠性探讨 对于前端工程师来说,在HTML、CSS和JavaScript开发中借助AI工具是司空见惯的事情。然而,并非所有工具都能提供同等的可靠性。 个性化需求 关于哪个AI工具最可靠,这个问题没有一刀切的答案。每个人的使用习惯和项目需求各不相同。以下是一些影响选择的重要因素: 立…

    2025年12月24日
    300
  • 如何用 CSS Paint API 实现倾斜的斑马线间隔圆环?

    实现斑马线边框样式:探究 css paint api 本文将探究如何使用 css paint api 实现倾斜的斑马线间隔圆环。 问题: 给定一个有多个圆圈组成的斑马线图案,如何使用 css 实现倾斜的斑马线间隔圆环? 答案: 立即学习“前端免费学习笔记(深入)”; 使用 css paint api…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信