您当前的位置:首页 > 时尚 > 内容

如何用代码画圆环(画圆的代码)

如何用代码画圆环(画圆的代码)?如果你对这个不了解,来看看!

前端高级之路:写一个高逼格可视化“圆环”,下面是大前端晨曦给大家的分享,一起来看看。

如何用代码画圆环

日常生产生活中,我们会经常读到或使用各种类型的图表。圆环(圆弧)便是一种较常见的类型,用于直观展现某一数据指标占整体的比例。本文以 HTML Canvas 的实现为主(当然,SVG 党可以在了解原理后自行实现),逐层介绍圆环图表开发的一些主要思路和原理。

图1 所示是一些我们平时比较常见的一些圆环(圆弧)效果。虽然图形的主体构成都是圆弧,但不同效果在信息传达的功能上却略有差异。如:

闭合的圆环可以表示流程 “进度” 的概念非闭合圆环一般用于状态量(标量)的展示,一般也称为 “仪表盘” 效果不同的色相可用于标识状态量的状态区间(如:低危-中危-高危 区间的标识可以使用三种颜色的构成的过渡效果进行表达)

为了更加方便、完善地解决我们在业务开发时的具体需要,可以对这些风格、样式进行一定分析、抽象,总结出一个通用组件需要具备的能力,如:

颜色、渐变可配(支持传入单一色值或颜色序列)圆弧宽度可调(内、外半径大小可配置)圆弧夹角可调(开始角度、截止角度可配置)圆弧端点造型可选(可切换平角/半圆造型文案效果可调(字号、字体、颜色等)

下面,我们着手于实现这样一个功能全面、业务通用性较强的圆环组件。

1. 圆环的造型

绘制圆环造型的第一步,需要先绘制圆环图表构成要素,即一段一段的圆弧。而对于像下图中这样的两种倒角效果(黄色部分圆弧两端的样式),既可以是直角,也可以是半圆。

因此,我们需要实现一个通用的方法来绘制圆弧,提供两种倒角风格给用户。

1.1 前景圆弧的绘制

圆弧绘制的思路如上图所示,按先后顺序大致分为几个步骤:

(1)绘制圆弧起始端的半圆轮廓

(2)绘制圆弧的外边缘轮廓

(3)绘制圆弧终止端的半圆轮廓

(4)绘制圆弧的内边缘轮廓

(5)闭合轮廓并填充色彩

注:由于 canvas 绘制圆弧的方法默认是顺时针方向,因而我们的绘图步骤也是沿着顺时针方向

以下是一些姿势要领:

1.1.1 端点坐标的计算

在绘制端点半圆之前我们需要端点的位置坐标,以其实端为例,根据圆环的半径(内外径的均值,即圆弧中线的半径)和起始端点的角度如何计算圆上一点的坐标:

//计算圆弧上某点的坐标//originX,originY-圆心的坐标//radius-圆环半径,等于圆环内、外径的平均值,也即圆弧中线的半径//alpha-弧度functioncalcPosition(originX,originY,radius,alpha){return[radius*Math.cos(alpha)+originX,radius*Math.sin(alpha)+originY,];}1.1.2 端点半圆的起止角度

在 canvas 中绘制一个 arc,需要知道其起始角度和终止角度。由于 canvas 绘制默认方向为屏幕顺时针方向(屏幕 Z轴 的左手螺旋方向),从上面的示意图中可以看出:

起始端半圆弧度范围 - [radianStart - Math.PI, radianStart]

终止端半圆弧度范围 - [radianEnd, radianEnd + Math.PI]

1.1.3 绘制半圆

有了端点坐标和起止角度,便可以绘制端点的半圆:

//以起始端的半圆倒角为例myCanvas.context.arc(x,y,(radiusOutter-radiusInner)/2,//小圆半径,等于圆环线宽的一半radianStart-Math.PI,radianStart);

直角倒角风格的绘制与半圆倒角圆弧的绘制步骤基本相同,主要差别在于不用绘制圆弧两个端点的小半圆,改成绘制直线。背景圆弧的绘制也与前景圆弧方法一致。

2. Canvas 实现锥形渐变

上面的步骤可以绘制出圆弧的轮廓,要达到 图1 那样的视觉效果,我们需要给前面绘制出来的轮廓填充图像。

沿着圆周方向的渐变,因为其图像形似圆锥体的俯瞰效果,俗称锥形渐变:

众所周知,CSS 中有一个名为 conic-gradient 的属性直接支持锥形渐变,而 HTML Canvas 的原生 API 目前还没有类似的能力。那么,我们如何在 canvas 中绘制出这样的图像呢?

下面我们讲下大致的原理:

(1)对用户传入的颜色进行插值,得到一个颜色序列。

这里,我们直接使用 canvas 原生的 createLinearGradient 方法,在离屏 canvas 中绘制一个 1px 的线性渐变效果,图像宽度正好是我们要插值的数量,渐变插值的结果也就是 canvas 上对应像素位置的色值。

颜色插值(渐变取色)代码实现如下:

//用于实现颜色插值的工具类exportdefaultclassColorInterpolate{//参数01:stops-为要插值的颜色序列,数据格式形如:[[0,'red'],[0.5,'green'],[1.0,'yellow']]//参数02:segment-插值段落数,即插值结果的颜色值的数量constructor(stops=[],segment=100){//构建离屏canvasconstcanvas=document.createElement('canvas');canvas.width=segment;canvas.height=1;this.ctx=canvas.getContext('2d');//绘制线性渐变constgradient=this.ctx.createLinearGradient(0,0,segment,0);for(let[offset,color]ofstops){gradient.addColorStop(offset,color);}this.ctx.fillStyle=gradient;this.ctx.fillRect(0,0,segment,1);}//根据位置偏移量获取插值后的色值getColor(offset){constimgData=this.ctx.getImageData(offset,0,1,1);return`rgba(${imgData.data.slice(0,3).join(',')},${imgData.data[3]/255})`;}}

(2)如下图所示,我们可以把渐变的图像看成是由足够多填充了单个色值的小 “扇面” 拼接而成。

按照这样的思路,我们只需要遍历上面色彩插值得到的各个颜色,然后逐个绘制小扇面,便可得到一个锥形渐变图像。

为此我们封装了一个名为 createConicalGradient 的方法,其使用习惯与 canvas 原生的 createLinearGradient 和 createRadialGradient 方法相似。具体代码见 我的 Github(觉得有用的童鞋可以 star 一下)。

3. 过渡动画3.1 圆弧的过渡

在数值发生改变时,我们的图表需要一个能够跟随数据改变的过渡动画效果,对于 canvas 而言,便是清除旧图像然后绘制新一帧图像。这里有一些方法包装上的技巧:

//注:伪代码,真实场景建议OOP方式包装为工具类let_animTick=null;let_animFrames=null;let_frameData=null;let_animDiff=null;//动画方法function_animate(duration){if(_animTick===null){//根据动画时长duration计算整个动画一共需要多少帧(以60fps计算)_animFrames=Math.round((duration/1e3)*60);//相邻两帧动画的数据变化_animDiff=_calcAnimDiff(_animFrames);//动画帧数标识_animTick=0;}//当前帧的数据值_frameData=_caclCurentData(_animDiff,_animTick);_renderFrame(_frameData);if(_animTick!==null&&_animTick<_animFrames){//继续执行动画window.requestAnimationFrame(()=>{_animate();_animTick+=1;});}else{//动画结束_renderFrame(_frameData);_animTick=null;}}//绘制当前帧function_renderFrame(data){//...}//计算动画相邻帧的数据差异function_calcAnimDiff(){//...}//根据两帧数据差计算当前帧function_caclCurentData(){//...}3.2 两种动画模式

在过渡动画执行的过程中,需要考虑两种不同的模式:一种是渐变图像不变化,仅是圆弧的轮廓从旧状态变化到新状态;一种是渐变图像的夹角范围跟随轮廓的大小改变。

这两种模式其实都有一定意义:前者可以使用不同颜色代表数值的不同状态;后者仅仅是将渐变的颜色看成一种装饰效果。

4. 结果演示

比较关键的原理都介绍完了,最后展示一下我们封装的图表组件的效果(右侧 GUI 部分是我们自研的设计引擎的编辑效果):

5. 写在后面

本文是可视化图表开发的一个小案例,。篇幅比较短,还有很多细节没有展开讨论。感兴趣的童鞋欢迎交流、留言!

画圆的代码

这篇文章主要为大家详细介绍了C语言实现俄罗斯方块小游戏,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

1.要先下载一个 graphics.h 的头文件来绘图。

2.初始化窗口:initgraph(x, y);这是先创建一个窗口的函数,以左上角为(0,0),向右为x轴,向下为y轴,其中x表示长x个单位,y表示宽y个单位。

3.关闭图像窗口:closegraph();结束时用来关闭用的。

4.按任意键继续:getch();这个就和getchar();差不多,为了防止以运行完就关了,这样能停顿一下,他的头文件是:conio.h 。

5.画线:line(x1, y1, x2, y2);在你创建的那么窗口里以(x1,y1)和(x2,y2)为两个端点画线。

6.画矩形:rectangle(x1,y1,x2,y2);以(x1,y1)和(x2,y2)为对角画一个矩形。

7.画圆:circle(x,y,r);以(x,y)为圆点,r为半径画圆。

8.颜色:setcolor(x);用来设置颜色的,其中x是你要设置的颜色,可以填这16种:黑 BLACK、蓝 BLUE、绿 GREEN、青 CYAN、红 RED、紫 MAGENTA、棕 BROWN、浅灰 LIGHTGRAY、深灰 DARKGRAY、亮蓝 LIGHTBLUE、亮绿 LIGHTGREEN、亮青 LIGHTCYAN、亮红 LIGHTRED、亮紫 LIGHTMAGENTA、黄 YELLOW、白 WHITE;当然,你也可以根据光的三原色来调自己喜欢的颜色,方法是:setcolor(RGB(x,y,z));其中RGB分别代表红绿蓝,对应的x,y,z是你选的该颜色的多少,范围是[0,255]。

#include <stdio.h>

#include <windows.h>

#include <conio.h>

#include <time.h>

//游戏窗口

#define FrameX 4 //游戏窗口左上角的X轴坐标

#define FrameY 4 //游戏窗口左上角的Y轴坐标

#define Frame_height 20 //游戏窗口的高度

#define Frame_width 18 //游戏窗口的宽度

//定义全局变量

int i,j,temp,temp1,temp2; //temp,temp1,temp2用于记住和转换方块变量的值

int a[80][80]={0}; //标记游戏屏幕的图案:2,1,0分别表示该位置为游戏边框、方块、无图案;初始化为无图案

int b[4]; //标记4个"口"方块:1表示有方块,0表示无方块

//声明俄罗斯方块的结构体

struct Tetris

{

int x; //中心方块的x轴坐标

int y; //中心方块的y轴坐标

int flag; //标记方块类型的序号

int next; //下一个俄罗斯方块类型的序号

int speed; //俄罗斯方块移动的速度

int count; //产生俄罗斯方块的个数

int score; //游戏的分数

int level; //游戏的等级

};

//函数原型声明

//光标移到指定位置

void gotoxy(HANDLE hOut, int x, int y);

//制作游戏窗口

void make_frame();

//随机产生方块类型的序号

void get_flag(struct Tetris *);

//制作俄罗斯方块

void make_tetris(struct Tetris *);

//打印俄罗斯方块

void print_tetris(HANDLE hOut,struct Tetris *);

//清除俄罗斯方块的痕迹

void clear_tetris(HANDLE hOut,struct Tetris *);

//判断是否能移动,返回值为1,能移动,否则,不动

int if_moveable(struct Tetris *);

//判断是否满行,并删除满行的俄罗斯方块

void del_full(HANDLE hOut,struct Tetris *);

//开始游戏

void start_game();

void main()

{

//制作游戏窗口

make_frame();

//开始游戏

start_game();

}

/******光标移到指定位置**************************************************************/

void gotoxy(HANDLE hOut, int x, int y)

{

COORD pos;

pos.X = x; //横坐标

pos.Y = y; //纵坐标

SetConsoleCursorPosition(hOut, pos);

}

/******制作游戏窗口******************************************************************/

void make_frame()

{

HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); //定义显示器句柄变量

gotoxy(hOut,FrameX+Frame_width-5,FrameY-2); //打印游戏名称

printf("俄罗斯方块");

gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+7); //打印选择菜单

printf("**********下一个方块:");

gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+13);

printf("**********");

gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+17);

printf("↑键:变体");

gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+19);

printf("空格:暂停游戏");

gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+15);

printf("Esc :退出游戏");

gotoxy(hOut,FrameX,FrameY); //打印框角并记住该处已有图案

printf("╔");

gotoxy(hOut,FrameX+2*Frame_width-2,FrameY);

printf("╗");

gotoxy(hOut,FrameX,FrameY+Frame_height);

printf("╚");

gotoxy(hOut,FrameX+2*Frame_width-2,FrameY+Frame_height);

printf("╝");

a[FrameX][FrameY+Frame_height]=2;

a[FrameX+2*Frame_width-2][FrameY+Frame_height]=2;

for(i=2;i<2*Frame_width-2;i+=2)

{

gotoxy(hOut,FrameX+i,FrameY);

printf("═"); //打印上横框

}

for(i=2;i<2*Frame_width-2;i+=2)

{

gotoxy(hOut,FrameX+i,FrameY+Frame_height);

printf("═"); //打印下横框

a[FrameX+i][FrameY+Frame_height]=2; //记住下横框有图案

}

for(i=1;i<Frame_height;i++)

{

gotoxy(hOut,FrameX,FrameY+i);

printf("║"); //打印左竖框

a[FrameX][FrameY+i]=2; //记住左竖框有图案

}

for(i=1;i<Frame_height;i++)

{

gotoxy(hOut,FrameX+2*Frame_width-2,FrameY+i);

printf("║"); //打印右竖框

a[FrameX+2*Frame_width-2][FrameY+i]=2; //记住右竖框有图案

}

}

/******制作俄罗斯方块********************************************************************/

void make_tetris(struct Tetris *tetris)

{

a[tetris->x][tetris->y]=b[0]; //中心方块位置的图形状态:1-有,0-无

switch(tetris->flag) //共6大类,19种类型

{

case 1: //田字方块

{

a[tetris->x][tetris->y-1]=b[1];

a[tetris->x+2][tetris->y-1]=b[2];

a[tetris->x+2][tetris->y]=b[3];

break;

}

case 2: //直线方块:----

{

a[tetris->x-2][tetris->y]=b[1];

a[tetris->x+2][tetris->y]=b[2];

a[tetris->x+4][tetris->y]=b[3];

break;

}

case 3: //直线方块: |

{

a[tetris->x][tetris->y-1]=b[1];

a[tetris->x][tetris->y-2]=b[2];

a[tetris->x][tetris->y+1]=b[3];

break;

}

case 4: //T字方块

{

a[tetris->x-2][tetris->y]=b[1];

a[tetris->x+2][tetris->y]=b[2];

a[tetris->x][tetris->y+1]=b[3];

break;

}

case 5: //T字顺时针转90度方块

{

a[tetris->x][tetris->y-1]=b[1];

a[tetris->x][tetris->y+1]=b[2];

a[tetris->x-2][tetris->y]=b[3];

break;

}

case 6: //T字顺时针转180度方块

{

a[tetris->x][tetris->y-1]=b[1];

a[tetris->x-2][tetris->y]=b[2];

a[tetris->x+2][tetris->y]=b[3];

break;

}

case 7: //T字顺时针转270度方块

{

a[tetris->x][tetris->y-1]=b[1];

a[tetris->x][tetris->y+1]=b[2];

a[tetris->x+2][tetris->y]=b[3];

break;

}

case 8: //Z字方块

{

a[tetris->x][tetris->y+1]=b[1];

a[tetris->x-2][tetris->y]=b[2];

a[tetris->x+2][tetris->y+1]=b[3];

break;

}

case 9: //Z字顺时针转90度方块

{

a[tetris->x][tetris->y-1]=b[1];

a[tetris->x-2][tetris->y]=b[2];

a[tetris->x-2][tetris->y+1]=b[3];

break;

}

case 10: //Z字顺时针转180度方块

{

a[tetris->x][tetris->y-1]=b[1];

a[tetris->x-2][tetris->y-1]=b[2];

a[tetris->x+2][tetris->y]=b[3];

break;

}

case 11: //Z字顺时针转270度方块

{

a[tetris->x][tetris->y+1]=b[1];

a[tetris->x+2][tetris->y-1]=b[2];

a[tetris->x+2][tetris->y]=b[3];

break;

}

case 12: //7字方块

{

a[tetris->x][tetris->y-1]=b[1];

a[tetris->x][tetris->y+1]=b[2];

a[tetris->x-2][tetris->y-1]=b[3];

break;

}

case 13: //7字顺时针转90度方块

{

a[tetris->x-2][tetris->y]=b[1];

a[tetris->x-2][tetris->y+1]=b[2];

a[tetris->x+2][tetris->y]=b[3];

break;

}

case 14: //7字顺时针转180度方块

{

a[tetris->x][tetris->y-1]=b[1];

a[tetris->x][tetris->y+1]=b[2];

a[tetris->x+2][tetris->y+1]=b[3];

break;

}

case 15: //7字顺时针转270度方块

{

a[tetris->x-2][tetris->y]=b[1];

a[tetris->x+2][tetris->y-1]=b[2];

a[tetris->x+2][tetris->y]=b[3];

break;

}

case 16: //倒7字方块

{

a[tetris->x][tetris->y+1]=b[1];

a[tetris->x][tetris->y-1]=b[2];

a[tetris->x+2][tetris->y-1]=b[3];

break;

}

case 17: //倒7字顺指针转90度方块

{

a[tetris->x-2][tetris->y]=b[1];

a[tetris->x-2][tetris->y-1]=b[2];

a[tetris->x+2][tetris->y]=b[3];

break;

}

case 18: //倒7字顺时针转180度方块

{

a[tetris->x][tetris->y-1]=b[1];

a[tetris->x][tetris->y+1]=b[2];

a[tetris->x-2][tetris->y+1]=b[3];

break;

}

case 19: //倒7字顺时针转270度方块

{

a[tetris->x-2][tetris->y]=b[1];

a[tetris->x+2][tetris->y+1]=b[2];

a[tetris->x+2][tetris->y]=b[3];

break;

}

}

}

//******判断是否可动*************************************************************************/

int if_moveable(struct Tetris *tetris)

{

if(a[tetris->x][tetris->y]!=0)//当中心方块位置上有图案时,返回值为0,即不可移动

{

return 0;

}

else

{

if( //当为田字方块且除中心方块位置外,其他"口"字方块位置上无图案时,返回值为1,即可移动

( tetris->flag==1 && ( a[tetris->x][tetris->y-1]==0 &&

a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||

//或为直线方块且除中心方块位置外,其他"口"字方块位置上无图案时,返回值为1,即可移动

( tetris->flag==2 && ( a[tetris->x-2][tetris->y]==0 &&

a[tetris->x+2][tetris->y]==0 && a[tetris->x+4][tetris->y]==0 ) ) ||

( tetris->flag==3 && ( a[tetris->x][tetris->y-1]==0 &&

a[tetris->x][tetris->y-2]==0 && a[tetris->x][tetris->y+1]==0 ) ) ||

( tetris->flag==4 && ( a[tetris->x-2][tetris->y]==0 &&

a[tetris->x+2][tetris->y]==0 && a[tetris->x][tetris->y+1]==0 ) ) ||

( tetris->flag==5 && ( a[tetris->x][tetris->y-1]==0 &&

a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y]==0 ) ) ||

( tetris->flag==6 && ( a[tetris->x][tetris->y-1]==0 &&

a[tetris->x-2][tetris->y]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||

( tetris->flag==7 && ( a[tetris->x][tetris->y-1]==0 &&

a[tetris->x][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||

( tetris->flag==8 && ( a[tetris->x][tetris->y+1]==0 &&

a[tetris->x-2][tetris->y]==0 && a[tetris->x+2][tetris->y+1]==0 ) ) ||

( tetris->flag==9 && ( a[tetris->x][tetris->y-1]==0 &&

a[tetris->x-2][tetris->y]==0 && a[tetris->x-2][tetris->y+1]==0 ) ) ||

( tetris->flag==10 && ( a[tetris->x][tetris->y-1]==0 &&

a[tetris->x-2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||

( tetris->flag==11 && ( a[tetris->x][tetris->y+1]==0 &&

a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||

( tetris->flag==12 && ( a[tetris->x][tetris->y-1]==0 &&

a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y-1]==0 ) ) ||

( tetris->flag==13 && ( a[tetris->x-2][tetris->y]==0 &&

a[tetris->x-2][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||

( tetris->flag==14 && ( a[tetris->x][tetris->y-1]==0 &&

a[tetris->x][tetris->y+1]==0 && a[tetris->x+2][tetris->y+1]==0 ) ) ||

( tetris->flag==15 && ( a[tetris->x-2][tetris->y]==0 &&

a[tetris->x+2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||

( tetris->flag==16 && ( a[tetris->x][tetris->y+1]==0 &&

a[tetris->x][tetris->y-1]==0 && a[tetris->x+2][tetris->y-1]==0 ) ) ||

( tetris->flag==17 && ( a[tetris->x-2][tetris->y]==0 &&

a[tetris->x-2][tetris->y-1]==0 && a[tetris->x+2][tetris->y]==0 ) ) ||

( tetris->flag==18 && ( a[tetris->x][tetris->y-1]==0 &&

a[tetris->x][tetris->y+1]==0 && a[tetris->x-2][tetris->y+1]==0 ) ) ||

( tetris->flag==19 && ( a[tetris->x-2][tetris->y]==0 &&

a[tetris->x+2][tetris->y+1]==0 && a[tetris->x+2][tetris->y]==0 ) ) )

{

return 1;

}

}

return 0;

}

/******随机产生俄罗斯方块类型的序号**********************************************************/

void get_flag(struct Tetris *tetris)

{

tetris->count++; //记住产生方块的个数

srand((unsigned)time(NULL)); //初始化随机数

if(tetris->count==1)

{

tetris->flag = rand()%19+1; //记住第一个方块的序号

}

tetris->next = rand()%19+1; //记住下一个方块的序号

}

/******打印俄罗斯方块**********************************************************************/

void print_tetris(HANDLE hOut,struct Tetris *tetris)

{

for(i=0;i<4;i++)

{

b[i]=1; //数组b[4]的每个元素的值都为1

}

make_tetris(tetris); //制作俄罗斯方块

for( i=tetris->x-2; i<=tetris->x+4; i+=2 )

{

for(j=tetris->y-2;j<=tetris->y+1;j++)

{

if( a[i][j]==1 && j>FrameY )

{

gotoxy(hOut,i,j);

printf("□"); //打印边框内的方块

}

}

}

//打印菜单信息

gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+1);

printf("level : %d",tetris->level);

gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+3);

printf("score : %d",tetris->score);

gotoxy(hOut,FrameX+2*Frame_width+3,FrameY+5);

printf("speed : %dms",tetris->speed);

}

以上就是分享给大家的俄罗斯方块源码,希望对大家有帮助~

最后,学习C/C++编程知识,想要成为一个更加优秀的程序员,或者你学习C/C++的时候有难度,可以关注+私信小编【C/C++编程】,里面不仅有学习视频和文件资料,还有更多志同道合的朋友,和大家一起交流成长会比自己琢磨更快哦!


声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,谢谢。

上一篇: 格瑞美厨从烹饪到家居装修,一站式解决方法

下一篇: 牛肉粉丝汤热量(牛肉粉丝)



猜你感兴趣

推荐阅读

网站内容来自网络,如有侵权请联系我们,立即删除! | 软文发布 | 粤ICP备2021106084号