dotnet OpenXML PPT 动画框架入门

本文深入探讨如何使用C#和OpenXML库处理PPT的动画框架,包括主序列动画、单个动画存储、动画触发顺序等方面。通过实例代码展示了如何解析和操作PPT的动画元素。
摘要由CSDN通过智能技术生成

本文将从 OpenXML 方面聊 PPT 的动画框架,本文是属于编程方面而不是 PPT 动画制作教程

开始之前,还请掌握一些基础知识,如阅读以下博客

本文不讨论 Slide Master 和 Slide Layout 的动画,关于这两个请参阅 dotnet OpenXML 的 Slide Master 和 Slide Layout 是什么

本文只讨论 Slide 页面里面的动画

元素主序列动画

在 OpenXML 中,如果一个动画是依靠翻页或点击页面进行触发的,那么这些动画有顺序的触发,这部分就是主序列动画,也叫 主动画序列 在 OpenXML 的 PPTX 文件里面的存放大概如下

  <p:timing>
    <p:tnLst>
      <p:par>
        <p:cTn id="1" dur="indefinite" restart="never" nodeType="tmRoot">
          <p:childTnLst>
            <p:seq concurrent="1" nextAc="seek">
              <p:cTn id="2" dur="indefinite" nodeType="mainSeq">
              </p:cTn>
            </p:seq>
          </p:childTnLst>
        </p:cTn>
      </p:par>
    </p:tnLst>
  </p:timing>

动画是存放在 Slide 页面里面的 Timing 属性里面,通过 OpenXML SDK 获取方法如下

            using var presentationDocument =
                DocumentFormat.OpenXml.Packaging.PresentationDocument.Open("Test.pptx", false);
            var presentationPart = presentationDocument.PresentationPart;
            var slidePart = presentationPart!.SlideParts.First();
            var slide = slidePart.Slide;
            var timing = slide.Timing;

默认的动画将会放在 NodeType 为 TmingRoot 的 cTn 也就是 CommonTimeNode 里面,获取代码如下

            var slide = slidePart.Slide;
            var timing = slide.Timing;
            // 第一级里面默认只有一项
            var commonTimeNode = timing?.TimeNodeList?.ParallelTimeNode?.CommonTimeNode;

            if (commonTimeNode?.NodeType?.Value == TimeNodeValues.TmingRoot)
            {
                // 这是符合约定
                // nodeType="tmRoot"
            }

按照约定,页面里面的动画将放在 TmingRoot 的里层,而元素的主序列动画也属于页面里面的动画,因此也就放在 TmingRoot 的里层

如上面代码就是 nodeType="mainSeq" 主序列动画的定义,获取主序列动画的代码如下

            // <p:timing>
            //    <p:tnLst>
            //      <p:par>
            //        <p:cTn id="1" dur="indefinite" restart="never" nodeType="tmRoot">
            // 第一级里面默认只有一项
            var commonTimeNode = timing?.TimeNodeList?.ParallelTimeNode?.CommonTimeNode;

            if (commonTimeNode?.NodeType?.Value == TimeNodeValues.TmingRoot)
            {
                // 这是符合约定
                // nodeType="tmRoot"
            }

            if (commonTimeNode?.ChildTimeNodeList == null) return;
            // <p:childTnLst>
            //   <p:seq concurrent="1" nextAc="seek">
            // 理论上只有一项,而且一定是 SequenceTimeNode 类型
            var sequenceTimeNode = commonTimeNode.ChildTimeNodeList.GetFirstChild<SequenceTimeNode>();

            // <p:cTn id="2" dur="indefinite" nodeType="mainSeq">
            var mainSequenceTimeNode = sequenceTimeNode.CommonTimeNode;
            if (mainSequenceTimeNode?.NodeType?.Value == TimeNodeValues.MainSequence)

接下来讨论的就是放在主序列动画里面的动画的存储方式,以上代码放在 githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 2c06ddf74e45c31ad7842dd06dc09bcc67b6142e

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 PptxDemo 文件夹

单个主序列动画

放在主序列动画里面的单个动画,创建方式如新建一个 PPT 文件,然后拖入一个形状,点击一下飞入动画。此时的飞入动画就是属于放在主动画序列的一个动画,当然飞入动画在类型上属于进入动画。在 PPT 里面,有 进入动画、强调动画、退出动画等类型

以下是单个飞入动画的主序列动画的 OpenXML 文档的例子

  <p:timing>
    <p:tnLst>
      <p:par>
        <p:cTn id="1" dur="indefinite" restart="never" nodeType="tmRoot">
          <p:childTnLst>
            <p:seq concurrent="1" nextAc="seek">
              <p:cTn id="2" dur="indefinite" nodeType="mainSeq">
                <p:childTnLst>
                  <p:par>
                    <p:cTn id="3" fill="hold">
                      <p:stCondLst>
                        <p:cond delay="indefinite" />
                      </p:stCondLst>
                      <p:childTnLst>
                        <p:par>
                          <p:cTn id="4" fill="hold">
                            <p:stCondLst>
                              <p:cond delay="0" />
                            </p:stCondLst>
                            <p:childTnLst>
                              <p:par>
                                <p:cTn id="5" presetID="2" presetClass="entr" presetSubtype="4" fill="hold" grpId="0" nodeType="clickEffect">
                                   <!-- 飞入动画 -->
                                </p:cTn>
                              </p:par>
                            </p:childTnLst>
                          </p:cTn>
                        </p:par>
                      </p:childTnLst>
                    </p:cTn>
                  </p:par>
                </p:childTnLst>
              </p:cTn>
            </p:seq>
          </p:childTnLst>
        </p:cTn>
      </p:par>
    </p:tnLst>
  </p:timing>

可以看到单个动画放在单个主序列动画的两层 cTn 里面

如上面的内容,大概可以理解存放的方式了,只是在 PPT 里面,有多个 ParallelTimeNode 和 CommonTimeNode 的嵌套。从 mainSeq 也就是 MainSequence 主动画序列以下,获取到的实际的进入动画,是经过了如下路径才能获取

cTn (mainSeq) -> childTnLst -> par -> cTn (id="3") -> childTnLst -> par -> cTn (id="4") -> childTnLst -> par -> cTn (id="5" presetClass="entr" 飞入动画)

代码的获取方式如下

            // <p:cTn id="2" dur="indefinite" nodeType="mainSeq">
            var mainSequenceTimeNode = sequenceTimeNode.CommonTimeNode;
            if (mainSequenceTimeNode?.NodeType?.Value == TimeNodeValues.MainSequence)
            {
                // [TimeLine 对象 (PowerPoint) | Microsoft Docs](https://docs.microsoft.com/zh-cn/office/vba/api/PowerPoint.TimeLine )
                //  MainSequence 主动画序列
                var mainParallelTimeNode = mainSequenceTimeNode.ChildTimeNodeList;

                foreach (var openXmlElement in mainParallelTimeNode)
                {
                    // 并行关系的
                    if (openXmlElement is ParallelTimeNode parallelTimeNode)
                    {
                        var timeNode = parallelTimeNode.CommonTimeNode.ChildTimeNodeList
                            .GetFirstChild<ParallelTimeNode>().CommonTimeNode.ChildTimeNodeList
                            .GetFirstChild<ParallelTimeNode>().CommonTimeNode;

                        switch (timeNode.PresetClass.Value)
                        {
                            case TimeNodePresetClassValues.Entrance:
                                // 进入动画
                                break;
                            default:
                                throw new ArgumentOutOfRangeException();
                        }
                    }
                }
            }

以上测试文件和测试代码 放在 githubgitee 可以通过以下命令获取

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin d47f1aec803bfd7adb32e82fb61916308d317fcd

除了进入动画之外,还有强调和退出动画,详细请看 dotnet OpenXML 读取 PPT 动画进入退出强调动画类型

主序列顺序动画

新建 PPT 课件,添加一个元素,然后分别设置元素的进入强调和退出动画,然后设置强调和退出动画是从上一项之后开始,如下图

根据上文描述,可以了解到此时元素的进入和强调和退出类型动画都放在主序列动画里面,如下图

<p:cTn id="2" dur="indefinite" nodeType="mainSeq">
  <p:childTnLst>
    <p:par>
      <p:cTn id="3" fill="hold">
        <p:stCondLst>
          <p:cond delay="indefinite" />
        </p:stCondLst>
        <p:childTnLst>
          <p:par>
            <p:cTn id="4" fill="hold">
              <p:stCondLst>
                <p:cond delay="0" />
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值