新建了Direct2D中的资源后,接下来初始化用于绘制图形的应用窗口。在解决方案资源管理器窗口中右键点击项目图标,在弹出的菜单栏中选中"添加", 并在"添加"的子菜单栏中选择"新建项",在出现的"添加新项"窗口中选中"C++ 文件(.cpp)",添加名为"D2DBasicAnimation.cpp"的源文件。然后使用同样的方法在"添加新项"窗口中选中"头文件(.h)",添加名为"D2DBasicAnimation.h"的头文件。在本示例中用于激活应用窗口的实现代码,绘制几何图形的实现代码以及项目的主入口函数的实现代码将添加到D2DBasicAnimation.cpp源文件中,而D2DBasicAnimation.h头文件则用于声明在D2DBasicAnimation.cpp源文件中使用到的函数和变量。

添加了D2DBasicAnimation.h头文件和D2DBasicAnimation.cpp源文件以后,接下来打开D2DBasicAnimation.h头文件,并添加如下的代码定义一个D2DBasicAnimation类。

#include "DirectXBase.h"

#include "DirectXHelper.h"

//定义类D2DBasicAnimation,D2DBasicAnimation继承DirectXBase类和IFrameworkView接口

ref class D2DBasicAnimation : public DirectXBase, public Windows::ApplicationModel::Core::IFrameworkView

{

internal:

    //构造函数

    D2DBasicAnimation();

internal:

    // 重写DirectXBase中的CreateDeviceResources函数

    virtual void CreateDeviceResources() override;

public:

    // 实现 IFrameworkView 中的函数

    //激活应用窗口

    virtual void Initialize(_In_ Windows::ApplicationModel::Core::CoreApplicationView^ applicationView);

    //初始化应用窗口

    virtual void SetWindow(_In_ Windows::UI::Core::CoreWindow^ window);

    //在Run函数调用前加载视图所使用的资源

    virtual void Load(_In_ Platform::String^ entryPoint);

    //用于绘制三角形

    virtual void Run();

    virtual void Uninitialize();

};

在上面的代码中,首先使用include关键字引用DirectXBase.h头文件和DirectXHelper.h头文件,接着定义D2DBasicAnimation类,此类继承自DirectXBase类并实现IFrameworkView接口。在D2DBasicAnimation类中,声明构造函数D2DBasicAnimation,并重写DirectXBase类的CreateDeviceResources函数,此函数的实现代码将在第三部分进行介绍。在IFrameworkView接口中声明了Initialize函数、SetWindow函数、Load函数、Run函数和Uninitialize函数,为了实现这些函数,需要先在D2DBasicAnimation类中进行声明。

本部分将介绍Initialize函数、SetWindow函数、Load函数和Uninitialize函数的实现代码,其中SetWindow函数和Initialize函数分别用于初始化应用窗口和激活应用窗口,而在本示例中Run函数用于绘制几何图形,此函数的实现代码将在第三部分进行介绍。另外要注意的是为了避免程序出现异常,需要在CreateDeviceResources函数、Initialize函数、SetWindow函数、Load函数、Run函数和Uninitialize函数之前添加virtual关键字。

定义了D2DBasicAnimation类以后,接下来介绍如何初始化应用窗口。首先打开D2DBasicAnimation.cpp源文件,并引用如下的头文件和命名空间,在初始化和激活应用窗口时将会使用到定义在这些头文件和命名空间中的类。

#include "pch.h"

#include "D2DBasicAnimation.h"

using namespace Microsoft::WRL;

using namespace Windows::ApplicationModel;

using namespace Windows::ApplicationModel::Core;

using namespace Windows::ApplicationModel::Activation;

using namespace Windows::UI::Core;

using namespace Windows::System;

using namespace Windows::Foundation;

using namespace Windows::Graphics::Display;

using namespace D2D1;

引用上述的头文件和命名空间以后,接着在D2DBasicAnimation.cpp源文中添加Load函数和Uninitialize函数的实现代码,虽然在本示例中Load函数和Uninitialize函数并没有实现任何功能,但仍需要添加这两个函数的实现代码,保证项目运行时不会出现异常。代码如下所示:

//Load函数实现代码

void D2DBasicAnimation::Load(_In_ Platform::String^ entryPoint)

{

}

//Uninitialize函数实现代码

void D2DBasicAnimation::Uninitialize()

{

}

添加了Load函数和Uninitialize函数的实现代码以后,接下来在D2DBasicAnimation.cpp源文中添加SetWindow函数的实现代码,在此函数中将调用DirectXBase类的Initialize函数来初始化应用窗口。具体代码如下所示:

//初始化窗口

void D2DBasicAnimation::SetWindow(_In_ CoreWindow^ window)

{

    DirectXBase::Initialize(window, DisplayProperties::LogicalDpi);

}

本示例将DirectXBase类的Initialize函数的实现代码添加在DirectXBase.cpp源文件中,为了实现此函数,首先打开DirectXBase.h头文件,并在DirectXBase类中添加如下代码用来声明window变量和Initialize函数。

protected private:

    //声明成员变量window

    Platform::Agile<Windows::UI::Core::CoreWindow> window;

internal:

    //初始化应用窗口

    void Initialize(Windows::UI::Core::CoreWindow^ window, float dpi);

声明了Initialize函数以后,接着在DirectXBase.cpp源文件中添加Initialize函数的实现代码,具体代码如下所示:

//初始化应用窗口

void DirectXBase::Initialize(CoreWindow^ coreWindow, float dpi)

{

    //给window变量赋值

    window = coreWindow;

    //新建独立于设备的资源

    CreateDeviceIndependentResources();

    //新建依赖于设备的资源

    CreateDeviceResources();

    //设置Direct2D设备上下文的DPI值

    SetDpi(dpi);

}

在上面的代码中,首先将Initialize函数的参数coreWindow赋值给window变量,然后调用CreateDeviceIndependentResources函数和CreateDeviceResources函数分别新建独立于设备的资源和依赖于设备的资源,最后调用SetDpi函数来设置Direct2D设备上下文的DPI值,DPI值即每英寸的像素,Direct2D设备上下文包含了用于绘制图形的相关信息,包括每英寸的像素等。

添加了Initialize函数的实现代码以后,接下来实现上面的SetDpi函数。在DirectXBase.h头文件的DirectXBase类中添加如下代码,声明函数SetDpi和成员变量dpi、numBuffers。

protected private:

    //声明成员变量dpi

    float dpi;

    //声明成员变量numBuffers

    unsigned int numBuffers;

internal:

    //设置Direct2D设备上下文的DPI值

    void SetDpi(float dpi);

在初始化应用窗口的过程中将会用到dpi变量和numBuffers变量,首先需要为这两个变量赋初始值。在DirectXBase.cpp源文件中添加DirectXBase构造函数的实现代码,将dpi变量和numBuffers变量分别赋值为-1.0f和2,具体代码如下所示:

//DirectXBase构造函数

DirectXBase::DirectXBase() :

    dpi(-1.0f),

    numBuffers(2)

{

}

接着在DirectXBase.cpp源文件中添加SetDpi函数的实现代码,具体代码如下所示:

//设置Direct2D设备上下文的DPI值

void DirectXBase::SetDpi(float dpiParameter)

{

    //当前DPI值改变时更新DPI值

    if (dpiParameter!= dpi)

    {

        dpi = dpiParameter;

        //更新Direct2D设备上下文对象的DPI值

        d2dContext->SetDpi(dpi, dpi);

        //更新应用窗口的大小

        UpdateForWindowSizeChange();

    }

}

在上面的代码中,如果dpi变量保存的DPI值不等于参数dpiParameter保存的DPI值时,将参数dpiParameter赋值给dpi变量,并以dpi变量作为参数调用d2dContext指针所指向的对象的SetDpi函数来设置Direct2D设备上下文的DPI值。最后调用UpdateForWindowSizeChange函数更新应用窗口的大小。

添加了SetDpi函数的实现代码以后,接下来实现上面的UpdateForWindowSizeChange函数。首先在DirectXBase.h头文件的DirectXBase类中添加如下的代码:

protected private:

    //声明成员变量d2dTargetBitmap

    Microsoft::WRL::ComPtr<ID2D1Bitmap1> d2dTargetBitmap;

    //声明成员变量renderTargetView

    Microsoft::WRL::ComPtr<ID3D11RenderTargetView> renderTargetView;

    //声明成员变量depthStencilView

    Microsoft::WRL::ComPtr<ID3D11DepthStencilView> depthStencilView;

    //声明成员变量windowBounds

    Windows::Foundation::Rect windowBounds;

internal:

    //更新应用窗口的大小

    void UpdateForWindowSizeChange();

在上面的代码中,使用protected private关键字声明四个受保护的私有成员变量,分别为d2dTargetBitmap、renderTargetView、depthStencilView和windowBounds,其中d2dTargetBitmap为ID2D1Bitmap1类型的指针,renderTargetView为ID3D11RenderTargetView类型的指针,depthStencilView为ID3D11DepthStencilView类型的指针,windowBounds为Rect类型的变量。然后使用internal关键字声明内部访问的UpdateForWindowSizeChange函数。

声明了上述的成员变量和UpdateForWindowSizeChange函数以后,接着在DirectXBase.cpp源文件中添加UpdateForWindowSizeChange函数的实现代码,具体代码如下所示:

//更新应用窗口的大小

void DirectXBase::UpdateForWindowSizeChange()

{

    //当应用窗口大小改变时,更新应用窗口的大小,重新新建应用窗口资源

    if (window->Bounds.Width != windowBounds.Width || window->Bounds.Height != windowBounds.Height)

    {

        //将d2dTargetBitmap指针设为空指针

        d2dTargetBitmap = nullptr;

        //将renderTargetView指针设为空指针

        renderTargetView = nullptr;

        //将depthStencilView指针设为空指针

        depthStencilView = nullptr;

        //更新应用窗口的大小

        windowBounds = window->Bounds;

        //新建与应用窗口大小相关的资源

        CreateWindowSizeDependentResources();

    }

}

在上面的代码中,首先判断window->Bounds属性的Width成员和Height成员是否等于windowBounds变量的Width属性和Height属性,即应用窗口的大小是否发生改变。如果应用窗口的大小发生了改变,将d2dTargetBitmap指针、renderTargetView指针和depthStencilView指针都赋值为空指针,然后将windowBounds变量赋值为window->Bounds属性来更新应用窗口的大小,并调用CreateWindowSizeDependentResources函数新建与应用窗口大小相关的资源。

接下来实现上面的CreateWindowSizeDependentResources函数。首先在DirectXBase.h头文件的DirectXBase类中添加如下的代码,用来声明CreateWindowSizeDependentResources函数。

internal:

    //新建与应用窗口大小相关的资源

    void CreateWindowSizeDependentResources();

添加了CreateWindowSizeDependentResources函数的声明代码以后,在DirectXBase.cpp源文件中添加CreateWindowSizeDependentResources函数的实现代码,具体代码如下所示:

//新建与应用窗口大小相关的资源

void DirectXBase::CreateWindowSizeDependentResources()

{

    // 避免在大小相同的情况下重新生成所有内容

    if (swapChain != nullptr)

    {

        // 如果交换链已存在,调整其后台缓存大小

        DX::ThrowIfFailed(

            swapChain->ResizeBuffers(numBuffers, 0, 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0)

        );

    }

    else

    {

        // 否则,使用与现有 Direct3D 设备相同的适配器新建一个

        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};

        // 设置成员变量Width

        swapChainDesc.Width = 0;

        // 设置成员变量Height

        swapChainDesc.Height = 0;

        // 设置成员变量Format

        swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;

        // 设置成员变量Stereo

        swapChainDesc.Stereo = false;

        // 设置成员变量SampleDesc

        swapChainDesc.SampleDesc.Count = 1;

        swapChainDesc.SampleDesc.Quality = 0;

        // 设置成员变量BufferUsage

        swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;

        // 设置成员变量BufferCount

        swapChainDesc.BufferCount = numBuffers;

        // 设置成员变量Scaling

        swapChainDesc.Scaling = DXGI_SCALING_NONE;

        // 设置成员变量SwapEffect

        swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;

        // 设置成员变量Flags

        swapChainDesc.Flags = 0;

        // 创建Device设备

        ComPtr<IDXGIDevice1> dxgiDevice;

        DX::ThrowIfFailed(

            d3dDevice.As(&dxgiDevice)

        );

        // 创建适配器

        ComPtr<IDXGIAdapter> dxgiAdapter;

        DX::ThrowIfFailed(

            dxgiDevice->GetAdapter(&dxgiAdapter)

        );

        // 创建工厂

        ComPtr<IDXGIFactory2> dxgiFactory;

        DX::ThrowIfFailed(

    dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))

        );

        // 创建交换链

        CoreWindow^ coreWindow = window.Get();

        DX::ThrowIfFailed(

    dxgiFactory->CreateSwapChainForCoreWindow(d3dDevice.Get(),reinterpret_cast<IUnknown*>(coreWindow),&swapChainDesc,nullptr,&swapChain)

        );

        DX::ThrowIfFailed(

    dxgiDevice->SetMaximumFrameLatency(numBuffers-1)

        );

    }

    // 创建交换链后台缓冲区的呈现目标视图

    ComPtr<ID3D11Texture2D> backBuffer;

    DX::ThrowIfFailed(

        swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))

    );

    // 创建渲染目标

    DX::ThrowIfFailed(

    d3dDevice->CreateRenderTargetView(backBuffer.Get(),nullptr,&renderTargetView)

    );

    D3D11_TEXTURE2D_DESC backBufferDesc = {0};

    backBuffer->GetDesc(&backBufferDesc);

    CD3D11_TEXTURE2D_DESC depthStencilDesc(DXGI_FORMAT_D24_UNORM_S8_UINT,backBufferDesc.Width,backBufferDesc.Height,1,1,D3D11_BIND_DEPTH_STENCIL);

    //创建一组2D纹理

    ComPtr<ID3D11Texture2D> depthStencil;

    DX::ThrowIfFailed(

    d3dDevice->CreateTexture2D(&depthStencilDesc,nullptr,&depthStencil)

    );

    //创建深度视图

    CD3D11_DEPTH_STENCIL_VIEW_DESC viewDesc = CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D);

    DX::ThrowIfFailed(

    d3dDevice->CreateDepthStencilView(depthStencil.Get(),&viewDesc,&depthStencilView)

    );

    //设置用于确定整个窗口的呈现视区

    CD3D11_VIEWPORT viewport(0.0f,0.0f,static_cast<float>(backBufferDesc.Width),static_cast<float>(backBufferDesc.Height));

    d3dContext->RSSetViewports(1, &viewport);

    D2D1_BITMAP_PROPERTIES1 bitmapProperties = BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),dpi,dpi);

    ComPtr<IDXGISurface> dxgiBackBuffer;

    DX::ThrowIfFailed(

        swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))

    );

    DX::ThrowIfFailed(

    d2dContext->CreateBitmapFromDxgiSurface(dxgiBackBuffer.Get(),&bitmapProperties,&d2dTargetBitmap)

    );

    d2dContext->SetTarget(d2dTargetBitmap.Get());

    //将文本抗锯齿模式设为使用灰度抗锯齿

    d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);

}

在上述的代码中,首先判断swapChain指针是否为空指针,如果swapChain指针不为空指针,调用swapChain指针所指向的对象的ResizeBuffers函数来调整交换链后台缓存的大小。这里的交换链主要用于更新前台缓存和后台缓存,使用Direct2D所绘制的图形保存在后台缓存中,前台缓存则用于将所绘制的图形显示到应用窗口中。

如果swapChain指针为空指针,定义一个DXGI_SWAP_CHAIN_DESC1结构体的变量swapChainDesc,并设置swapChainDesc结构体变量中的成员变量用来描述交换链中的信息。然后以swapChainDesc结构体变量作为参数调用CreateSwapChainForCoreWindow函数,得到一个IDXGISwapChain1类型的对象,使用swapChain指针指向这个对象。接着声明一个ID3D11Texture2D类型的指针backBuffer,并调用swapChain指针所指向的对象的GetBuffer函数来得到交换链的后台缓存对象,使用backBuffer指针指向这个后台缓存对象。然后以backBuffer指针作为参数调用CreateRenderTargetView函数得到一个ID3D11RenderTargetView类型的对象,使用renderTargetView指针指向这个对象。

接下来定义一个D3D11_TEXTURE2D_DESC结构体的变量backBufferDesc,并将此结构体变量的Width属性和Height属性赋值给renderTargetView指针所指向的对象的Width属性和Height属性。接着创建一个CD3D11_TEXTURE2D_DESC类的对象depthStencilDesc,并声明一个ID3D11Texture2D类型的指针depthStencil。然后以depthStencilDesc对象作为参数调用d3dDevice指针所指向的对象的CreateTexture2D函数得到一个ID3D11Texture2D类型的对象,使用depthStencil指针指向这个对象。

继续定义一个CD3D11_DEPTH_STENCIL_VIEW_DESC结构体的变量viewDesc,并以此结构体变量作为参数调用d3dDevice指针所指向的对象的CreateDepthStencilView函数,得到一个ID3D11DepthStencilView类型的对象,使用depthStencilView指针指向这个对象。然后创建一个CD3D11_VIEWPORT类的对象viewport,并将这个对象作为参数传递给d3dContext指针所指向的对象的RSSetViewports函数。

接着调用d2dContext指针所指向的对象的CreateBitmapFromDxgiSurface函数得到一个ID2D1Bitmap1类型的对象,使用d2dTargetBitmap指针指向这个对象,并将d2dTargetBitmap指针作为参数传递给d2dContext指针所指向的对象的SetTarget函数。最后调用d2dContext指针所指向的对象的SetTextAntialiasMode函数设置文本的抗锯齿模式为D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE,表示使用灰度抗锯齿。

初始化应用窗口以后,接下来激活这个应用窗口,这部分的实现代码将添加在D2DBasicAnimation.cpp源文件中。打开D2DBasicAnimation.cpp源文件,并添加Initialize函数的实现代码,为applicationView参数的Activated事件添加事件处理函数OnActivated用于激活应用窗口,具体代码如下所示:

void D2DBasicAnimation::Initialize(_In_ CoreApplicationView^ applicationView)

{

    //激活窗口,调用OnActivated方法

    applicationView->Activated +=

        ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &D2DBasicAnimation::OnActivated);

}

接下来在D2DBasicAnimation.h头文件的D2DBasicAnimation类中添加如下的代码,用来声明OnActivated函数。

private:

    //激活应用窗口

    void OnActivated(_In_ Windows::ApplicationModel::Core::CoreApplicationView^ applicationView,_In_ Windows::ApplicationModel::Activation::IActivatedEventArgs^ args);

添加了OnActivated函数的声明代码以后,接下来在D2DBasicAnimation.cpp源文件中添加OnActivated函数的实现代码,调用window指针所指向的对象的Activate函数来激活应用窗口,具体代码如下所示:

void D2DBasicAnimation::OnActivated(_In_ CoreApplicationView^ applicationView,_In_ IActivatedEventArgs^ args)

{

    //激活窗口

    window->Activate();

}

到此已经实现了初始化应用窗口和激活应用窗口,下面的部分将介绍如何在这个应用窗口中绘制三角形。

Win10系列:VC++绘制几何图形2的更多相关文章

  1. Win10系列:VC++绘制几何图形1

    本小节主要介绍如何使用Direct2D来绘制几何图形,其中会使用到FillGeometry函数和FillEllipse函数,FillGeometry函数用于填充几何图形的内部区域,而FillEllip ...

  2. Win10系列:VC++绘制几何图形5

    打开D2DBasicAnimation.h头文件,并在D2DBasicAnimation类中添加如下的代码: private:     //声明成员变量point     D2D1_POINT_2F ...

  3. Win10系列:VC++绘制几何图形4

    三角形绘制完成以后,接下来介绍如何给项目添加主入口函数.打开D2DBasicAnimation.h头文件,添加如下的代码定义一个DirectXAppSource类. //定义类DirectXAppSo ...

  4. Win10系列:VC++绘制几何图形3

    在绘制三角形之前,首先需要创建一个三角形,打开D2DBasicAnimation.h头文件,在D2DBasicAnimation类中添加如下的代码: private:     //声明成员变量obje ...

  5. Cocos2D中使用CCDrawNode绘制几何图形崩溃的解决

    在cocos2D v3.x中已经不能像在v2.x中那样直接调用ccDrawXXX函数来绘制几何图形了. 我们可以使用CCDrawNode或者CCRenderer来绘制图形. 但是官方的Api手册中说的 ...

  6. Win10系列:C#应用控件进阶9

    RectangleGeometry 在使用RectangleGeometry控件绘制矩形时,矩形的位置和尺寸由Rect属性定义,该属性指定矩形的相对位置.高度和宽度.Rect有四个参数,前两个参数表示 ...

  7. Win10系列:C#应用控件进阶6

    路径 路径(Path)可以用来定义任意形状的曲线和几何图形,当然这种任意性也带来了复杂性.为了方便的绘制几何图形,微软在Visual Studio 2012安装包中为程序开发者提供了免费的Blend ...

  8. Win10系列:C#应用控件进阶7

    PathGeometry 前面介绍了Path的使用方法,接下来介绍PathGeometry类.PathGeometry提供了描绘由弧线.曲线和直线组成的多个复杂图形的方法.PathGeometry的核 ...

  9. HTML5绘制几何图形

    <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head>    < ...

随机推荐

  1. python+unittet在linux与windows使用的区别

    使用python的unittest编写单元测试框架,批量运行测试用例时,如果使用discover时,windows环境下和linux环境下的代码不一样 Windows环境的run.py代码: case ...

  2. js几个小技巧和坑

    蝴蝶书看了,也知道充满了毒瘤和糟粕,但该用还是得用. 实际写了几天,小技巧记录下来.都是在py里有直接答案,不会遇到的问题,没想到js里这么费事. 还是要多读<ES6标准入门> 1判断ob ...

  3. jquery将表单序列化json对象

    $.fn.serializeObject = function () { var obj = {}; var count = 0; $.each(this.serializeArray(), func ...

  4. Asp.net core 学习笔记 ( DI 依赖注入 )

    比起 Angular 的依赖注入, core 的相对简单许多, 容易明白 所有 provider 都在 startup 里配置. public void ConfigureServices(IServ ...

  5. English Voice of << Count on me >>

    Count On Me 歌手:Bruno Mars 所属专辑:It´s Better If You Don´t Understand If you ever find yourself stuck i ...

  6. (GoRails)链接link_to到当前页current Page 并使用参数 (类ActionController::Parameters)

    https://gorails.com/episodes/rails-link-to-current-page-with-params?autoplay=1 如何链接到当前页并增加,移除,或者修改UR ...

  7. POJ-3107 Godfather 求每个节点连接的联通块数量

    dp[n][2],维护儿子的联通块数量和父亲的联通块数量. 第一遍dfs求儿子,第二遍dfs求爸爸. #include<iostream> #include<cstring> ...

  8. hadoop 企业应用案例--大众点评

    hadoop 企业应用案例--大众点评 http://f.dataguru.cn/thread-260531-1-1.html

  9. Confluence 6 为边栏添加自定义内容

    你可以使用 wiki 标记和自定义内容来对边栏进行更进一步的自定义. 希望添加自定义内容到你的边栏中: 进入空间后,然后从边栏的底部选择 空间工具(Space tools) > 外观和感觉(Lo ...

  10. stylus笔记

    Stylus介绍及特点 基于Node.js的css的预处理框架,其本质上做的事情与 Sass/LESS 等类似, 可以以近似脚本的方式去写CSS代码,创建健壮的.动态的.富有表现力的CSS,默认使用 ...