跳转至

2024年11月

获取ACAD背景色的两种方案:

  • 在这种情况下,我们可以使用函数**unsigned long acedGetRGB(int color);** 使用参数 0 来获取背景颜色。
  • 另一种选择是使用 acedGetCurrentColors 函数,您可以从中获取 dwGfxModelBkColor 和 dwGfxLayoutBkColor 并(取决于tilemode值)使用您需要的。

acedGetRGB

C++
void CWHASSET::drawWipeOut(AcDbExtents exts, AcGiWorldDraw *mode)
{
    if (showFramFlag())
    {
        AcGePoint3d minPt, maxPt;
        minPt = exts.minPoint();
        maxPt = exts.maxPoint();
        AcGePoint3dArray wipePnts;
        wipePnts.append(minPt);
        wipePnts.append(AcGePoint3d(minPt.x, maxPt.y, minPt.z));
        wipePnts.append(maxPt);
        wipePnts.append(AcGePoint3d(maxPt.x, minPt.y, maxPt.z));
        wipePnts.append(minPt);
        Adesk::UInt32 lColor = acedGetRGB(0);
        //UInt32需要转化成rgb数据
        int R = 0xFF & lColor;
        int G = 0xFF00 & lColor;
        G >>= 8;
        int B = 0xFF0000 & lColor;
        B >>= 16;AcCmEntityColor cmColor;
        cmColor.setRGB(R, G, B);
        mode->subEntityTraits().setTrueColor(cmColor);
        mode->subEntityTraits().setFillType(kAcGiFillAlways);
        mode->geometry().polygon(wipePnts.length(), wipePnts.asArrayPtr());
    }
}

acedGetCurrentColors

C++
void CWHASSET::drawWipeOut(AcDbExtents exts, AcGiWorldDraw *mode)
{
    if (showFramFlag())
    {
        AcGePoint3d minPt, maxPt;
        minPt = exts.minPoint();
        maxPt = exts.maxPoint();
        AcGePoint3dArray wipePnts;
        wipePnts.append(minPt);
        wipePnts.append(AcGePoint3d(minPt.x, maxPt.y, minPt.z));
        wipePnts.append(maxPt);
        wipePnts.append(AcGePoint3d(maxPt.x, minPt.y, maxPt.z));
        wipePnts.append(minPt);
        AcColorSettings colorSetings; 
        acedGetCurrentColors(&colorSetings);
        AcCmEntityColor bgColor;
        if (!mode->context()->isPlotGeneration())
        {
            COLORREF rgbColor = (COLORREF)colorSetings.dwGfxModelBkColor;
            int red = rgbColor & 255;
            int green = rgbColor >> 8 & 255;
            int blue = rgbColor >> 16 & 255;
            bgColor.setRed(red);
            bgColor.setBlue(blue);
            bgColor.setGreen(green);
            mode->subEntityTraits().setTrueColor(bgColor);
        }
        else
        {
            COLORREF rgbColor = (COLORREF)colorSetings.dwGfxLayoutBkColor;
            int red = rgbColor & 255;
            int green = rgbColor >> 8 & 255;
            int blue = rgbColor >> 16 & 255;
            bgColor.setRed(red);
            bgColor.setBlue(blue);
            bgColor.setGreen(green);
            mode->subEntityTraits().setTrueColor(bgColor);
        }

        mode->subEntityTraits().setFillType(kAcGiFillAlways);
        mode->geometry().polygon(wipePnts.length(), wipePnts.asArrayPtr());
    }
}

CSS学习第二天

CSS 的三大特性

CSS 有三个非常重要的三个特性:层叠性、继承性、优先级。

优先级

  1. 权重是有4组数字组成,但是不会有进位。
  2. 可以理解为类选择器永远大于元素选择器, id选择器永远大于类选择器,以此类推..
  3. 等级判断从左向右,如果某一位数值相同,则判断下一位数值。
  4. 可以简单记忆法: 通配符和继承权重为0, 标签选择器为1,类(伪类)选择器为 10, id选择器 100, 行内样式表为 1000, !important 无穷大.
  5. 继承的权重是0, 如果该元素没有直接选中,不管父元素权重多高,子元素得到的权重都是 0。

层叠性

CSS 层叠性口诀:长江后浪推前浪,前浪死在沙滩上。

  • 样式冲突,遵循的原则是就近原则,哪个样式离结构近,就执行哪个样式
  • 样式不冲突,不会层叠

继承性

继承性口诀:龙生龙,凤生凤,老鼠生的孩子会打洞

CSS中的继承: 子标签会继承父标签的某些样式,如文本颜色和字号。简单的理解就是:子承父业。

盒子模型

网页布局的核心本质: 就是利用 CSS 摆盒子

所谓 盒子模型:就是把 HTML 页面中的布局元素看作是一个矩形的盒子,也就是一个盛装内容的容器。 CSS 盒子模型本质上是一个盒子,封装周围的 HTML 元素,它包括:边框、外边距、内边距、和 实际内容

边框(border)

border可以设置元素的边框。边框有三部分组成:边框宽度(粗细) 边框样式 边框颜色

CSS
border : border-width || border-style || border-color
属性 作用
border-width 定义边框粗细,单位是px
border-style 边框的样式
border-color 边框颜色

边框样式 border-style 可以设置如下值:

  • none:没有边框即忽略所有边框的宽度(默认值)
  • solid:边框为单实线(最为常用的)
  • dashed:边框为虚线
  • dotted:边框为点线

demo:请给一个 200*200 的盒子,设置上边框为红色,其余边框为蓝色(提示:一定注意边框的层叠性)

HTML
1
2
3
4
5
6
7
8
9
<style>
    .box {
        width: 200px;
        height: 200px;
        border: 1px solid blue;
        border-top: 1px solid red;
    }
</style>
<div class="box"></div>

内边距(padding)

padding 属性用于设置内边距,即边框与内容之间的距离。

属性 表达意思
padding:5px; 1个值,代表上下左右都有5像素内边距
padding:5px 10px; 2个值,代表上下内边距是5像素,左右内边距是10像素;
padding:5px 10px 20px; 3个值,代表上内边距5像素,左边内边距10像素,下内边距20像素;
padding:5px 10px 20px 30px; 4个值,上、右、下、左,遵循顺时针
案例

image-20231214103810762

HTML
<style>
    .nav {
        height: 41px;
        border-top: 3px solid #ff8500;
        border-bottom: 1px solid #edeef0;
        background-color: #fcfcfc;
        line-height: 41px;
    }
    .nav a {
        /* a属于行内元素 此时必须要转换 行内块元素 */
        display: inline-block;
        height: 41px;
        padding: 0 20px;
        font-size: 12px;
        color: #4c4c4c;
        text-decoration: none;
    }
    .nav a:hover {
        background-color: #eee;
        color: #ff8500;
    }
</style>
<div class="nav">
    <a href="">设为首页</a>
    <a href="">手机新浪网</a>
    <a href="">移动客户端</a>
    <a href="">博客</a>
    <a href="">微博</a>
    <a href="">关注我们</a>
</div>

可以把display改成block和inline看看效果。

案例2

image-20231214113428560

HTML
<style>
    body {
    background-color: #f5f5f5;
    }
    .product {
    width: 298px;
    height: 415px;
    background-color: #fff;
    /* 让块级的盒子水平居中对齐 */
    margin: 100px auto;
    }
    .product .review {
    height: 70px;
    font-size: 14px;
    padding: 0 28px;
    margin-top: 30px;
    }
    .product img {
    /* 图片的宽度和父亲一样宽 */
    width: 100%;
    }
    .appraise {
    font-size: 12px;
    color: #b0b0b0;
    margin-top: 20px;
    padding: 0 28px;
    }
    .info {
    font-size: 14px;
    margin-top: 15px;
    padding: 0 28px;
    }
    .info span {
    color: #ff6700;    
    }
    .info em {
    font-style: normal;
    color: #ebe4e0;
    margin: 0 6px 0 15px;
    }
</style>
<div class="product">
    <img src="images/img.jpg" />
    <p class="review">快递牛,整理不错蓝牙可以说秒连。红米给力</p>
    <div class="appraise">来自于117384232的评价</div>
    <div class="info">
        Redmi AirDots真无线...
        <em>|</em>
        <span>99.00元</span>
    </div>
</div>

圆角边框

在 CSS3 中,新增了圆角边框样式,这样我们的盒子就可以变圆角了。

border-radius 属性用于设置元素的外边框圆角。

语法:

CSS
border-radius:length;
  • 参数值可以为数值或百分比的形式
  • 如果是正方形,想要设置为一个圆,把数值修改为高度或者宽度的一半即可,或者直接写为 50%
  • 该属性是一个简写属性,可以跟四个值,分别代表左上角、右上角、右下角、左下角
  • 分开写:border-top-left-radius、border-top-right-radius、border-bottom-right-radius 和border-bottom-left-radius
  • 兼容性 ie9+ 浏览器支持, 但是不会影响页面布局,可以放心使用.

盒子阴影

CSS3 中新增了盒子阴影,我们可以使用 box-shadow 属性为盒子添加阴影。

语法:

CSS
box-shadow: h-shadow v-shadow blur spread color inset;

image-20231214115203023

注意:

1.默认的是外阴影(outset), 但是不可以写这个单词,否则造成阴影无效 2.盒子阴影不占用空间,不会影响其他盒子排列。

文字阴影

在 CSS3 中,我们可以使用 text-shadow 属性将阴影应用于文本。

语法:

CSS
text-shadow: h-shadow v-shadow blur color;

image-20231214115243978

ARX将区域内实体转成BMP

C++
static void MyGroupTEST()
{
    //框选区域
    AcGePoint3d pt1, pt2;
    if (RTNORM != acedGetPoint(NULL, _T("\n第一角点"), asDblArray(pt1)))
        return;
    if (RTNORM != acedGetCorner(asDblArray(pt1), _T("\n第二角点"), asDblArray(pt2)))
        return;
    //用户坐标系转世界坐标系
    pt1 = TransformPoint(pt1, 1, 0);
    pt2 = TransformPoint(pt2, 1, 0);

    //pt1 = PublicFunction::TransUcs2Wcs(pt1);
    //pt2 = PublicFunction::TransUcs2Wcs(pt2);
    ads_name sset;
    if (RTNORM != acedSSGet(_T("W"), p1, p2, NULL, sset))
    {
        return;
    }
    Adesk::Int32 sslen;
    acedSSLength(sset, &sslen);
    ads_name ename;
    AcDbObjectId entId = AcDbObjectId::kNull;
    AcDbObjectIdArray idArray;
    idArray.removeAll();
    for (int i=0; i<sslen; i++)
    {
        acedSSName(sset, i, ename);
        acdbGetObjectId(entId, ename);
        idArray.append(entId);
    }

    ads_ssfree(sset);
    if (idArray.isEmpty())
        return;
    //bmp
    POINT cp1 = GetPointInAcadCoordinate(pt1);
    POINT cp2 = GetPointInAcadCoordinate(pt2);
    if (cp1.x > cp2.x)
        std::swap(cp1, cp2);
    WriteToBmp(cp1, cp2);
    //wblock
    AcDbDatabase* pShortCutDwg = NULL;
    acdbCurDwg()->wblock(pShortCutDwg, idArray, AcGePoint3d(0.0, 0.0, 0.0));
    pShortCutDwg->saveAs(_T("D:\\a.dwg"));
    delete pShortCutDwg;
    pShortCutDwg = NULL;

}

工具代码:

C++
AcGePoint3d TransformPoint(const AcGePoint3d &point, int nFromType, int nToType)
{
    AcGePoint3d pt;
    //从ucs转到wcs
    struct resbuf rbFrom, rbTo;
    rbFrom.restype = RTSHORT;
    rbFrom.resval.rint = nFromType; // from wcs
    rbTo.restype = RTSHORT;
    rbTo.resval.rint = nToType;     // from ucs
    acedTrans(asDblArray(point), &rbFrom, &rbTo, Adesk::kFalse, asDblArray(pt));
    return pt;
}

POINT GetPointInAcadCoordinate(const AcGePoint3d &pt)
{
    ads_point pnt;
    ads_point_set(asDblArray(pt), pnt);
    //获取视口编号
    int wndNum = 0;
    struct resbuf v;
    if (acedGetVar(_T("cvport"), &v) == RTNORM)
    {
        wndNum = v.resval.rint;
    }
    //将pt坐标转为pixel屏幕坐标
    CPoint ptInScreen;
    acedCoordFromWorldToPixel(wndNum, pnt, ptInScreen);
    //将ptInScreen转为在view上的点p3
    POINT p3;
    p3.x = ptInScreen.x; p3.y = ptInScreen.y;
    ::ClientToScreen(acedGetAcadDwgView()->m_hWnd, &p3);
    return p3;
}

void WriteToBmp(const POINT& pt1, const POINT& pt2)
{
    //获得屏幕的HWND
    HWND hWnd = ::GetDesktopWindow();
    //获得屏幕的HDC
    HDC hScreenDC = ::GetDC(hWnd);
    HDC MemDC = ::CreateCompatibleDC(hScreenDC);
    RECT rect;
    ::GetWindowRect(hWnd, &rect);
    SIZE screensize;
    screensize.cx = abs(pt1.x - pt2.x);
    screensize.cy = abs(pt1.y - pt2.y);
    HBITMAP hBitmap = ::CreateCompatibleBitmap(hScreenDC, screensize.cx, screensize.cy);
    HGDIOBJ hOldBMP = ::SelectObject(MemDC, hBitmap);
    ::BitBlt(MemDC, 0, 0, screensize.cx, screensize.cy, hScreenDC, pt1.x, pt1.y, SRCCOPY);
    ::SelectObject(MemDC, hOldBMP);
    ::DeleteObject(MemDC);
    ::ReleaseDC(hWnd, hScreenDC);

    HDC hDC = ::CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
    //当前分辨率下每个像素所占字节数
    int iBits = ::GetDeviceCaps(hDC, BITSPIXEL) * ::GetDeviceCaps(hDC, PLANES);
    ::DeleteDC(hDC);
    //位图中每个像素所占字节数
    WORD wBitCount;
    if (iBits <= 1)
        wBitCount = 1;
    else if (iBits <= 4)
        wBitCount = 4;
    else if (iBits <= 8)
        wBitCount = 8;
    else if (iBits <= 24)
        wBitCount = 24;
    else
        wBitCount = iBits;
    //调色板大小, 位图中像素字节大小 
    DWORD dwPaletteSize = 0;
    if (wBitCount <= 8)
        dwPaletteSize = (1 << wBitCount) * sizeof(RGBQUAD);

    //位图属性结构
    BITMAP bm;
    ::GetObject(hBitmap, sizeof(bm), (LPSTR)&bm);

    //位图信息头结构
    BITMAPINFOHEADER bi;
    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bm.bmWidth;
    bi.biHeight = bm.bmHeight;
    bi.biPlanes = 1;
    bi.biBitCount = wBitCount;
    //BI_RGB表示位图没有压缩
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    DWORD dwBmBitsSize = ((bm.bmWidth * wBitCount + 31) / 32) * 4 * bm.bmHeight;
    //为位图内容分配内存
    HANDLE hDib = ::GlobalAlloc(GHND, dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
    LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
    *lpbi = bi;
    // 处理调色板 
    HANDLE hPal = ::GetStockObject(DEFAULT_PALETTE);
    HANDLE  hOldPal = NULL;
    if (hPal)
    {
        hDC = ::GetDC(NULL);
        hOldPal = SelectPalette(hDC, (HPALETTE)hPal, FALSE);
        RealizePalette(hDC);
    }
    // 获取该调色板下新的像素值
    ::GetDIBits(hDC, hBitmap, 0, (UINT)bm.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) + dwPaletteSize, (BITMAPINFO*)lpbi, DIB_RGB_COLORS);
    //恢复调色板
    if (hOldPal)
    {
        SelectPalette(hDC, (HPALETTE)hOldPal, TRUE);
        RealizePalette(hDC);
        ::ReleaseDC(NULL, hDC);
    }
    //位图文件头结构
    BITMAPFILEHEADER bmfHdr;
    // "BM"
    bmfHdr.bfType = 0x4D42;
    // 设置位图文件头
    DWORD dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;
    bmfHdr.bfSize = dwDIBSize;
    bmfHdr.bfReserved1 = 0;
    bmfHdr.bfReserved2 = 0;
    bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;
    //创建位图文件
    HANDLE hFile = CreateFile(_T("D:\\a.bmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    DWORD dwWritten;
    // 写入位图文件头
    WriteFile(hFile, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
    // 写入位图文件其余内容
    WriteFile(hFile, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);
    //清除
    GlobalUnlock(hDib);
    GlobalFree(hDib);
    CloseHandle(hFile);
}

雪花算法ID到前端之后精度丢失问题

前后端交互的时候,数据的ID字段采用的雪花ID,Long类型,返回给前端时,由于数值过大,会导致精度丢失,后面几位会变成0,这时候就需要把ID字段转成String类型的返回给前端页面。

@JSONField

采用@JSONField注解,在ID字段上加上如下的注解,即可返回前端字符串的ID数据

Java
@JSONField(serializeUsing = ToStringSerializer.class)

Java

缺点:很明显,每个返回实体 model,只要有ID,就需要添加注解,太过于繁琐

方式二、Long.class转成String.class

在全局配置中,将Long类型的字段转成String类型

Java
@Configuration
public class CustomFastJsonConfig {
    @Bean
    FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
        //1.需要定义一个convert转换消息的对象
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();

        //2.添加fastJson的配置信息
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        //3.设置Long为字符串
        SerializeConfig serializeConfig = SerializeConfig.globalInstance;
        serializeConfig.put(Long.class, ToStringSerializer.instance);
        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
        fastJsonConfig.setSerializeConfig(serializeConfig);

        //4.在convert中添加配置信息.
        converter.setFastJsonConfig(fastJsonConfig);
        return converter;
    }
}

Java

缺点:也很明显,后端返回的数据,只要是Long类型的字段,都会被转成String返回,导致扩大了转换范围

SerializeFilter

利用FastJson内置的SerializeFilter,有很多,如下

  • PropertyPreFilter 根据PropertyName判断是否序列化;
  • PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化;
  • NameFilter 修改Key,如果需要修改Key,process返回值则可;
  • ValueFilter 修改Value;
  • BeforeFilter 序列化时在最前添加内容;
  • AfterFilter 序列化时在最后添加内容。

这里主要用到的SerializeFilter为ValueFilter,如下:

Java
public HttpMessageConverter fastJsonHttpMessageConverters() {
        //1.需要定义一个Convert转换消息的对象
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");

        SerializeConfig.globalInstance.put(Long.class, ToStringSerializer.instance);

        fastJsonConfig.setSerializeConfig(SerializeConfig.globalInstance);

        //3.设置id字段为字符串
        fastJsonConfig.setSerializeFilters((ValueFilter) (object, name, value) -> {
            if ("id".equalsIgnoreCase(name)){
                return value + "";
            }
            return value;
        });
        fastConverter.setFastJsonConfig(fastJsonConfig);
        HttpMessageConverter<?> converter = fastConverter;
        return converter;
    }

Java

很明显,这个处理方式是最好的,这里是将ID字段转成String,需要转换其他字段时,只需要新增相应的逻辑判断即可

其它Jackson的解决方案

前端用String类型的雪花ID保持精度,后端及数据库继续使用Long(BigINT)类型不影响数据库查询执行效率。

剩下的问题就是:在Spring Boot应用中,使用Jackson进行JSON序列化的时候怎么将Long类型ID转成String响应给前端。方案如下:

Java
@Configuration
public class JacksonConfig {

  @Bean
  @Primary
  @ConditionalOnMissingBean(ObjectMapper.class)
  public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
  {
    ObjectMapper objectMapper = builder.createXmlMapper(false).build();

    // 全局配置序列化返回 JSON 处理
    SimpleModule simpleModule = new SimpleModule();
    //JSON Long ==> String
    simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
    objectMapper.registerModule(simpleModule);
    return objectMapper;
  }
}

雪花算法计算唯一的UUID

利用现成的算法https://github.com/sniper00/snowflake-cpp github开源算法

A CPP port of Twitter's Snowflake id generation algorithm

Use

C++
1
2
3
4
5
6
7
8
9
    using snowflake_t = snowflake<1534832906275L>;
    snowflake_t uuid;
    uuid.init(1, 1);

    for (int64_t i = 0; i < 10000; ++i)
    {
        auto id = uuid.nextid();
        std::cout << id << "\n";
    }

CPP

Use with lock

C++
1
2
3
4
5
6
7
8
9
    using snowflake_t = snowflake<1534832906275L,std::mutex>;
    snowflake_t uuid;
    uuid.init(1, 1);

    for (int64_t i = 0; i < 10000; ++i)
    {
        auto id = uuid.nextid();
        std::cout << id << "\n";
    }

CPP

snowflake-cpp

C++
#pragma once
#include <cstdint>
#include <chrono>
#include <stdexcept>
#include <mutex>

class snowflake_nonlock
{
public:
    void lock()
    {
    }
    void unlock()
    {
    }
};

template<int64_t Twepoch, typename Lock = snowflake_nonlock>
class snowflake
{
    using lock_type = Lock;
    static constexpr int64_t TWEPOCH = Twepoch;
    static constexpr int64_t WORKER_ID_BITS = 5L;
    static constexpr int64_t DATACENTER_ID_BITS = 5L;
    static constexpr int64_t MAX_WORKER_ID = (1 << WORKER_ID_BITS) - 1;
    static constexpr int64_t MAX_DATACENTER_ID = (1 << DATACENTER_ID_BITS) - 1;
    static constexpr int64_t SEQUENCE_BITS = 12L;
    static constexpr int64_t WORKER_ID_SHIFT = SEQUENCE_BITS;
    static constexpr int64_t DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    static constexpr int64_t TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
    static constexpr int64_t SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1;

    using time_point = std::chrono::time_point<std::chrono::steady_clock>;

    time_point start_time_point_ = std::chrono::steady_clock::now();
    int64_t start_millsecond_ = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

    int64_t last_timestamp_ = -1;
    int64_t workerid_ = 0;
    int64_t datacenterid_ = 0;
    int64_t sequence_ = 0;
    lock_type lock_;
public:
    snowflake() = default;

    snowflake(const snowflake&) = delete;

    snowflake& operator=(const snowflake&) = delete;

    void init(int64_t workerid, int64_t datacenterid)
    {
        if (workerid > MAX_WORKER_ID || workerid < 0) {
            throw std::runtime_error("worker Id can't be greater than 31 or less than 0");
        }

        if (datacenterid > MAX_DATACENTER_ID || datacenterid < 0) {
            throw std::runtime_error("datacenter Id can't be greater than 31 or less than 0");
        }

        workerid_ = workerid;
        datacenterid_ = datacenterid;
    }

    int64_t nextid()
    {
        std::lock_guard<lock_type> lock(lock_);
        //std::chrono::steady_clock  cannot decrease as physical time moves forward
        auto timestamp = millsecond();
        if (last_timestamp_ == timestamp)
        {
            sequence_ = (sequence_ + 1)&SEQUENCE_MASK;
            if (sequence_ == 0)
            {
                timestamp = wait_next_millis(last_timestamp_);
            }
        }
        else
        {
            sequence_ = 0;
        }

        last_timestamp_ = timestamp;

        return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
            | (datacenterid_ << DATACENTER_ID_SHIFT)
            | (workerid_ << WORKER_ID_SHIFT)
            | sequence_;
    }

private:
    int64_t millsecond() const noexcept
    {
        auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time_point_);
        return start_millsecond_ + diff.count();
    }

    int64_t wait_next_millis(int64_t last) const noexcept
    {
        auto timestamp = millsecond();
        while (timestamp <= last)
        {
            timestamp = millsecond();
        }
        return timestamp;
    }
};

day09-前端基础CSS第七天

学习目标:

​ 能够使用精灵图

​ 能够使用字体图标

​ 能够写出 CSS 三角

​ 能够写出常见的 CSS 用户界面样式

​ 能够说出常见的布局技巧

1. 精灵图(重点)

1.1 为什么需要精灵图

1571482435259

一个网页中往往会应用很多小的背景图像作为修饰,当网页中的图像过多时,服务器就会频繁地接收和发送请求图片,造成服务器请求压力过大,这将大大降低页面的加载速度。

为什么使用精灵图(目的):

为了有效地减少服务器接收和发送请求的次数提高**页面的**加载速度,出现了 CSS 精灵技术(也称 CSS Sprites、CSS 雪碧)。

核心原理

​ 将网页中的一些小背景图像整合到一张大图中 ,这样服务器只需要一次请求就可以了。

精灵图举例:

1571519067073

1.2 精灵图(sprites)的使用

使用精灵图核心:

  1. 精灵技术主要针对于背景图片使用。就是把多个小背景图片整合到一张大图片中。
  2. 这个大图片也称为 sprites 精灵图 或者 雪碧图
  3. 移动背景图片位置, 此时可以使用 background-position 。
  4. 移动的距离就是这个目标图片的 x 和 y 坐标。注意网页中的坐标有所不同
  5. 因为一般情况下都是往上往左移动,所以数值是负值。
  6. 使用精灵图的时候需要精确测量,每个小背景图片的大小和位置。

使用精灵图核心总结:

  1. 精灵图主要**针对于小的背景图片**使用。

  2. 主要借助于背景位置来实现---background-position

  3. 一般情况下精灵图都是**负值**。(千万注意网页中的坐标: x轴右边走是正值,左边走是负值, y轴同理。)

1.3 案例:拼出自己名字

1.3.1 案例效果

1571519368229

1.3.2 代码参考

结构

HTML
1
2
3
4
<span class="p">p</span>
<span class="i">i</span>
<span class="n">n</span>
<span class="k">k</span>

样式

CSS
span {
    display: inline-block;
    background: url(images/abcd.jpg) no-repeat;
}
.p {
    width: 100px;
    height: 112px;
    /* background-color: pink; */
    background-position:  -493px -276px;
}
.i {
    width: 60px;
    height: 108px;
    /* background-color: pink; */
    background-position: -327px -142px;
}
.n {
    width: 108px;
    height: 109px;
    /* background-color: pink; */
    background-position: -215px -141px;
}
.k {
    width: 105px;
    height: 114px;
    /* background-color: pink; */
    background-position: -495px -142px;
}

2. 字体图标

2.1 字体图标的产生

字体图标使用场景: 主要用于显示网页中通用、常用的一些小图标。

精灵图是有诸多优点的,但是缺点很明显。

1.图片文件还是比较大的。

2.图片本身放大和缩小会失真。

3.一旦图片制作完毕想要更换非常复杂。

此时,有一种技术的出现很好的解决了以上问题,就是**字体图标 iconfont**。

字体图标**可以为前端工程师提供一种方便高效的图标使用方式,**展示的是图标,本质属于字体

2.2 字体图标的优点

轻量级:一个图标字体要比一系列的图像要小。一旦字体加载了,图标就会马上渲染出来,减少了服务器请求

  • 灵活性:本质其实是文字,可以很随意的改变颜色、产生阴影、透明效果、旋转等
  • 兼容性:几乎支持所有的浏览器,请放心使用
  • 注意: 字体图标不能替代精灵技术,只是对工作中图标部分技术的提升和优化。

总结:

1.如果遇到一些结构和样式比较简单的小图标,就用字体图标。1571519724473

2.如果遇到一些结构和样式复杂一点的小图片,就用精灵图。

1571519739775

使用步骤

字体图标是一些网页常见的小图标,我们直接网上下载即可。 因此使用可以分为:

1.字体图标的下载

2.字体图标的引入 (引入到我们html页面中)

3.字体图标的追加 (以后添加新的小图标)

2.3 字体图标的下载

推荐下载网站:

IcoMoon 成立于 2011 年,推出了第一个自定义图标字体生成器,它允许用户选择所需要的图标,使它们成一字型。该字库内容种类繁多,非常全面,唯一的遗憾是国外服务器,打开网速较慢。

这个是阿里妈妈 M2UX 的一个 iconfont 字体图标字库,包含了淘宝图标库和阿里妈妈图标库。可以使用 AI制作图标上传生成。 重点是,免费!

2.4 字体图标的引入

下载完毕之后,注意原先的文件不要删,后面会用

  1. 把下载包里面的 fonts 文件夹放入页面根目录下

1571520092646

  • 字体文件格式

不同浏览器所支持的字体格式是不一样的,字体图标之所以兼容,就是因为包含了主流浏览器支持的字体文件。

1).TureType( .ttf )格式.ttf字体是Windows和Mac的最常见的字体,支持这种字体的浏览器有IE9+、Firefox3.5+、Chrome4+、Safari3+、Opera10+、iOS Mobile、Safari4.2+;

2).Web Open Font Format( .woff )格式woff字体,支持这种字体的浏览器有IE9+、Firefox3.5+、Chrome6+、Safari3.6+、Opera11.1+;

3).Embedded Open Type( .eot )格式.eot字体是IE专用字体,支持这种字体的浏览器有IE4+;

4).SVG( .svg )格式.svg字体是基于SVG字体渲染的一种格式,支持这种字体的浏览器有Chrome4+、Safari3.1+、Opera10.0+、iOS Mobile Safari3.2+;

2.在 CSS 样式中全局声明字体: 简单理解把这些字体文件通过css引入到我们页面中。

一定注意字体文件路径的问题

Text Only
 @font-face {
   font-family: 'icomoon';
   src:  url('fonts/icomoon.eot?7kkyc2');
   src:  url('fonts/icomoon.eot?7kkyc2#iefix') format('embedded-opentype'),
     url('fonts/icomoon.ttf?7kkyc2') format('truetype'),
     url('fonts/icomoon.woff?7kkyc2') format('woff'),
     url('fonts/icomoon.svg?7kkyc2#icomoon') format('svg');
   font-weight: normal;
   font-style: normal;
 }
  1. html 标签内添加小图标。

1571520411345

  1. 给标签定义字体。
Text Only
1
2
3
 span {
   font-family: "icomoon";
 }

注意:务必保证 这个字体和上面@font-face里面的字体保持一致

1571520485350

2.5 字体图标的追加

如果工作中,原来的字体图标不够用了,我们需要添加新的字体图标到原来的字体文件中。

把压缩包里面的 selection.json 从新上传,然后选中自己想要新的图标,从新下载压缩包,并替换原来的文件即可。

1571520554317

2.6 字体图标加载的原理:

1571520617270

3. CSS 三角

3.1 介绍

网页中常见一些三角形,使用 CSS 直接画出来就可以,不必做成图片或者字体图标。

一张图, 你就知道 CSS 三角是怎么来的了, 做法如下:

1571520965966

CSS
1
2
3
4
5
6
7
8
 div {
    width: 0; 
    height: 0;
    border: 50px solid transparent;
    border-color: red green blue black;
    line-height:0;
    font-size: 0;
 }
  1. 我们用css 边框可以模拟三角效果
  2. 宽度高度为0
  3. 我们4个边框都要写, 只保留需要的边框颜色,其余的不能省略,都改为 transparent 透明就好了
  4. 为了照顾兼容性 低版本的浏览器,加上 font-size: 0; line-height: 0;

3.2 案例:京东三角

3.2.1效果图

1571521183026

3.2.2 代码参考
HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>CSS 三角制作</title>
    <style>
        .box1 {
            width: 0;
            height: 0;
            /* border: 10px solid pink; */
            border-top: 10px solid pink;
            border-right: 10px solid red;
            border-bottom: 10px solid blue;
            border-left: 10px solid green;
        }
        .box2 {
            width: 0;
            height: 0;
            border: 50px solid transparent;
            border-left-color: pink;
            margin: 100px auto;
        }
        .jd {
            position: relative;
            width: 120px;
            height: 249px;
            background-color: pink;
        }
        .jd span {
            position: absolute;
            right: 15px;
            top: -10px;
            width: 0;
            height: 0;
            /* 为了照顾兼容性 */
            line-height: 0;  
            font-size: 0;
            border: 5px solid transparent;
            border-bottom-color: pink;
        }
    </style>
</head>
<body>
    <div class="box1"></div>
    <div class="box2"></div>
    <div class="jd">
        <span></span>
    </div>
</body>
</html>

4. CSS 用户界面样式

什么是界面样式

所谓的界面样式,就是更改一些用户操作样式,以便提高更好的用户体验。

  • 更改用户的鼠标样式
  • 表单轮廓
  • 防止表单域拖拽

4.1 鼠标样式 cursor

CSS
1
2
3
 li {
    cursor: pointer; 
 }

设置或检索在对象上移动的鼠标指针采用何种系统预定义的光标形状。

1571521805183

4.2 轮廓线 outline

给表单添加 outline: 0; 或者 outline: none; 样式之后,就可以去掉默认的蓝色边框。

CSS
1
2
3
 input {
    outline: none; 
 }

4.3 防止拖拽文本域 resize

实际开发中,我们文本域右下角是不可以拖拽的。

CSS
1
2
3
 textarea{ 
    resize: none;
 }

vertical-align 属性应用

5. vertical-align 属性应用

CSS 的 vertical-align 属性使用场景: 经常用于设置图片或者表单(行内块元素)和文字垂直对齐。

官方解释: 用于设置一个元素的**垂直对齐方式**,但是它只针对于行内元素或者行内块元素有效。

语法:

CSS
vertical-align : baseline | top | middle | bottom 

1571522023413

1571522040645

5.1 图片、表单和文字对齐

图片、表单都属于行内块元素,默认的 vertical-align 是基线对齐。

1571522093729

此时可以给图片、表单这些行内块元素的 vertical-align 属性设置为 middle 就可以让文字和图片垂直居中对齐了。

5.2 解决图片底部默认空白缝隙问题

bug:图片底侧会有一个空白缝隙,原因是行内块元素会和文字的基线对齐。

主要解决方法有两种:

1.给图片**添加 **vertical-align:middle | top| bottom 等。 (提倡使用的)

2.把图片转换为块级元素 display: block;

1571522162225

6. 溢出的文字省略号显示

6.1 单行文本溢出显示省略号

1571522317809

单行文本溢出显示省略号--必须满足三个条件:

CSS
1
2
3
4
5
6
7
8
  /*1. 先强制一行内显示文本*/
   white-space: nowrap;   默认 normal 自动换行

  /*2. 超出的部分隐藏*/
   overflow: hidden;

  /*3. 文字用省略号替代超出的部分*/
   text-overflow: ellipsis;

6.2 多行文本溢出显示省略号(了解)

1571522334591

多行文本溢出显示省略号,有较大兼容性问题,适合于webKit浏览器或移动端(移动端大部分是webkit内核)

CSS
/*1. 超出的部分隐藏 */
overflow: hidden;

/*2. 文字用省略号替代超出的部分 */
text-overflow: ellipsis;

/* 3. 弹性伸缩盒子模型显示 */
display: -webkit-box;

/* 4. 限制在一个块元素显示的文本的行数 */
-webkit-line-clamp: 2;

/* 5. 设置或检索伸缩盒对象的子元素的排列方式 */
-webkit-box-orient: vertical;

更推荐让后台人员来做这个效果,因为后台人员可以设置显示多少个字,操作更简单。

7. 常见布局技巧

巧妙利用一个技术更快更好的布局:

  1. margin负值的运用
  2. 文字围绕浮动元素
  3. 行内块的巧妙运用
  4. CSS三角强化

7.1. margin负值运用

1571522666082

1571522683897

1.让每个盒子margin 往左侧移动 -1px 正好压住相邻盒子边框

2.鼠标经过某个盒子的时候,提高当前盒子的层级即可(如果没有有定位,则加相对定位(保留位置),如果有定位,则加z-index)

7.2 文字围绕浮动元素

效果

1571522777745

布局示意图

1571522761996

巧妙运用浮动元素不会压住文字的特性

7.3 行内块巧妙运用

1571522898744

页码在页面中间显示:

  1. 把这些链接盒子转换为行内块, 之后给父级指定 text-align:center;
  2. 利用行内块元素中间有缝隙,并且给父级添加 text-align:center; 行内块元素会水平会居中

1571522910580

7.4. CSS 三角强化 案例

7.4.1 原理

1571550959181

1571551000391

1571548058053

HTML
<style>
    .box1 {
        width: 0;
        height: 0;

        /* 把上边框宽度调大 */
        /* border-top: 100px solid transparent;
        border-right: 50px solid skyblue; */
        /* 左边和下边的边框宽度设置为0 */
        /* border-bottom: 0 solid blue;
        border-left: 0 solid green; */

        /* 1.只保留右边的边框有颜色 */
        border-color: transparent red transparent transparent;
        /* 2. 样式都是solid */
        border-style: solid;
        /* 3. 上边框宽度要大, 右边框 宽度稍小, 其余的边框该为 0 */
        border-width: 100px 50px 0 0 ;
    }

</style>
</head>
<body>
    <div class="box1"></div>
</body>
7.4.2 案例效果

1571548099631

7.4.3 代码参考
HTML
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>CSS三角强化的巧妙运用</title>
        <style>
            .price {
                width: 160px;
                height: 24px;
                line-height: 24px;
                border: 1px solid red;
                margin: 0 auto;
            }
            .miaosha {
                position: relative;
                float: left;
                width: 90px;
                height: 100%;
                background-color:red;
                text-align: center;
                color: #fff;
                font-weight: 700;
                margin-right: 8px;

            }
            .miaosha i {
                position: absolute;
                right: 0;
                top: 0;
                width: 0;
                height: 0;
                border-color: transparent #fff transparent transparent;
                border-style: solid;
                border-width: 24px 10px 0 0;
            }
            .origin {
                font-size: 12px;
                color: gray;
                text-decoration: line-through;
            }
        </style>
    </head>
    <body>
        <div class="price">
            <span class="miaosha">
                ¥1650
                <i></i>
            </span>
            <span class="origin">¥5650</span>
        </div>
    </body>
</html>

8. CSS 初始化

不同浏览器对有些标签的默认值是不同的,为了消除不同浏览器对HTML文本呈现的差异,照顾浏览器的兼容,我们需要对CSS 初始化

简单理解: CSS初始化是指重设浏览器的样式。 (也称为CSS reset)

每个网页都必须首先进行 CSS初始化。

这里我们以 京东CSS初始化代码为例。

Unicode编码字体:

把中文字体的名称用相应的Unicode编码来代替,这样就可以有效的避免浏览器解释CSS代码时候出现乱码的问题。

比如:

黑体 \9ED1\4F53 宋体 \5B8B\4F53 微软雅黑 \5FAE\8F6F\96C5\9ED1

day08-前端基础CSS第六天

今日目标

  • 能够说出 为什么要用定位
  • 能够说出 定位的 4 种分类
  • 能够说出 4 种定位各自的特点
  • 能够说出 为什么常用子绝父相布局
  • 能够写出 淘宝轮播图布局
  • 能够说出 显示隐藏的 3 种方式以及区别

1. 定位(position) 介绍

1.1 为什么使用定位

我们先来看一个效果,同时思考一下**用标准流或浮动能否实现类似的效果**?

场景1: 某个元素可以自由的在一个盒子内移动位置,并且压住其他盒子.

场景2:当我们滚动窗口的时候,盒子是固定屏幕某个位置的。

结论**:要实现以上效果,**标准流 或 浮动都无法快速实现

所以:

1.浮动可以让多个块级盒子一行没有缝隙排列显示, 经常用于横向排列盒子。

2.定位则是可以让盒子自由的在某个盒子内移动位置或者固定屏幕中某个位置,并且可以压住其他盒子。

1.2 定位组成

定位:将盒子**定**在某一个位置,所以**定位也是在摆放盒子, 按照定位的方式移动盒子**

定位也是用来布局的,它有两部分组成:

定位 = 定位模式 + 边偏移

定位模式 用于指定一个元素在文档中的定位方式。**边偏移**则决定了该元素的最终位置。

1.2.1 边偏移(方位名词)

边偏移 就是定位的盒子移动到最终位置。有 top、bottom、left 和 right 4 个属性。

边偏移属性 示例 描述
top top: 80px 顶端**偏移量,定义元素相对于其父元素**上边线的距离
bottom bottom: 80px 底部**偏移量,定义元素相对于其父元素**下边线的距离
left left: 80px 左侧**偏移量,定义元素相对于其父元素**左边线的距离
right right: 80px 右侧**偏移量,定义元素相对于其父元素**右边线的距离

定位的盒子有了边偏移才有价值。 一般情况下,凡是有定位地方必定有边偏移。

1.2.2 定位模式 (position)

在 CSS 中,通过 position 属性定义元素的**定位模式**,语法如下:

CSS
1
2
3
选择器 { 
    position: 属性值; 
}

定位模式是有不同分类的,在不同情况下,我们用到不同的定位模式。

定位模式决定元素的定位方式 ,它通过 CSS 的 position 属性来设置,其值可以分为四个:

语义
static **静态**定位
relative **相对**定位
absolute **绝对**定位
fixed **固定**定位

1.3 定位模式介绍

1.3.1. 静态定位(static) - 了解
  • 静态定位**是元素的**默认**定位方式,**无定位的意思。它相当于 border 里面的none,静态定位static,不要定位的时候用。

  • 语法:

Text Only
1
2
3
选择器 { 
    position: static; 
}
  • 静态定位 按照标准流特性摆放位置,它没有边偏移。

  • 静态定位在布局时我们几乎不用的

1.3.2. 相对定位(relative) - 重要
  • **相对定位**是元素在移动位置的时候,是相对于它自己**原来的位置**来说的(自恋型)。
  • 语法:
Text Only
1
2
3
选择器 { 
    position: relative; 
}
  • 相对定位的特点:(务必记住)

  • 1.它是相对于自己原来的位置来移动的(移动位置的时候参照点是自己原来的位置)。

  • 2.**原来**在标准流的**位置**继续占有,后面的盒子仍然以标准流的方式对待它。

    因此,相对定位并没有脱标。它最典型的应用是给绝对定位当爹的。。。

  • 效果图:

相对定位案例

1.3.3. 绝对定位(absolute) - 重要
1.3.3.1 绝对定位的介绍
  • **绝对定位**是元素在移动位置的时候,是相对于它**祖先元素**来说的(拼爹型)。

  • 语法:

Text Only
1
2
3
 选择器 { 
  position: absolute; 
 }
  1. 完全脱标 —— 完全不占位置;

  2. 父元素没有定位,则以**浏览器**为准定位(Document 文档)。

  1. 父元素要有定位

  2. 元素将依据最近的已经定位(绝对、固定或相对定位)的父元素(祖先)进行定位。

  • 绝对定位的特点总结:(务必记住)

1.如果**没有祖先元素**或者**祖先元素没有定位**,则以浏览器为基准定位(Document 文档)。

2.如果祖先元素有定位(相对、绝对、固定定位),则以最近一级的有定位祖先元素为参考点移动位置。

3.绝对定位**不再占有原先的位置**。所以绝对定位是脱离标准流的。(脱标)

1.3.3.2 定位口诀 —— 子绝父相

弄清楚这个口诀,就明白了绝对定位和相对定位的使用场景。

这个**“子绝父相”太重要了,是我们学习定位的**口诀,是定位中最常用的一种方式这句话的意思是:子级是绝对定位的话,父级要用相对定位。

因为绝对定位的盒子是拼爹的,所以要和父级搭配一起来使用。

①子级绝对定位,不会占有位置,可以放到父盒子里面的任何一个地方,不会影响其他的兄弟盒子。

②父盒子需要加定位限制子盒子在父盒子内显示。

③父盒子布局时,需要占有位置,因此父亲只能是相对定位。

这就是子绝父相的由来,所以**相对定位经常用来作为绝对定位的父级**。

总结: 因为父级需要占有位置,因此是相对定位, 子盒子不需要占有位置,则是绝对定位

当然,子绝父相不是永远不变的,如果父元素不需要占有位置,**子绝父绝**也会遇到。

疑问:为什么在布局时,**子级元素**使用**绝对定位**时,**父级元素**就要用**相对定位**呢?

观察下图,思考一下在布局时,**左右两个方向的箭头图片**以及**父级盒子**的定位方式。

子绝父相截图

分析

  1. 方向箭头**叠加在其他图片上方,应该使用**绝对定位,因为**绝对定位完全脱标**,完全不占位置。
  2. 父级盒子**应该使用**相对定位,因为**相对定位不脱标**,后续盒子仍然以标准流的方式对待它。
  3. 如果父级盒子也使用**绝对定位**,会完全脱标,那么下方的**广告盒子**会上移,这显然不是我们想要的。

结论父级要占有位置,子级要任意摆放,这就是**子绝父相**的由来。

1.3.4. 固定定位(fixed) - 重要
  • 固定定位**是元素**固定于浏览器可视区的位置。(认死理型) 主要使用场景: 可以在浏览器页面滚动时元素的位置不会改变。

  • 语法:

Text Only
1
2
3
 选择器 { 
  position: fixed; 
 }
  • 固定定位的特点:(务必记住):

1.以浏览器的可视窗口为参照点移动元素。

  • 跟父元素没有任何关系
  • 不随滚动条滚动。

2.固定定位**不在占有原先的位置**。

  • 固定定位也是**脱标**的,其实**固定定位也可以看做是一种特殊的绝对定位**。(认死理型)

  • 完全脱标—— 完全不占位置;

  • 只认**浏览器的可视窗口** —— 浏览器可视窗口 + 边偏移属性 来设置元素的位置;
    • 跟父元素没有任何关系;单独使用的
    • 不随滚动条滚动。

固定定位举例:

固定定位案例

提示:IE 6 等低版本浏览器不支持固定定位。

1.3.5 粘性定位(sticky) - 了解
  • **粘性定位**可以被认为是相对定位和固定定位的混合。 Sticky 粘性的

  • 语法:

Text Only
1
2
3
4
 选择器 { 
     position: sticky; 
     top: 10px; 
 }
  • 粘性定位的特点:

1.以浏览器的可视窗口为参照点移动元素(固定定位特点)

2.粘性定位占有原先的位置(相对定位特点)

3.必须添加 top 、left、right、bottom **其中一个**才有效

跟页面滚动搭配使用。 兼容性较差,IE 不支持。

1.3.6 定位总结
定位模式 是否脱标 移动位置 是否常用
static 静态定位 不能使用边偏移 很少
relative 相对定位 否 (占有位置) 相对于自身位置移动 基本单独使用
absolute绝对定位 是(不占有位置) 带有定位的父级 要和定位父级元素搭配使用
fixed 固定定位 是(不占有位置) 浏览器可视区 单独使用,不需要父级
sticky 粘性定位 否 (占有位置) 浏览器可视区 当前阶段少
  • 一定记住 相对定位、固定定位、绝对定位 两个大的特点: 1. 是否占有位置(脱标否) 2. 以谁为基准点移动位置。

  • 学习定位重点学会子绝父相。

  • 注意:

  • 边偏移**需要和**定位模式**联合使用,**单独使用无效

  • topbottom 不要同时使用;
  • leftright 不要同时使用。

2. 综合案例:学成在线-hot 模块添加

2. 1 案例截图

1571385860679

2.2 案例分析

  1. 一个大的 li 中包含 一个课程图片,课程介绍文字信息,还有hot的小图标;
  2. hot图片**重叠**在课程图片上面—— 脱标,不占位置,需要使用**绝对定位**;
  3. hot图片**重叠**在**li的右上方 —— 需要**使用边偏移确定准确位置。

2.3 案例小结

  1. 子绝父相 —— 子元素**使用**绝对定位父元素**使用**相对定位
  2. 与浮动的对比
  3. 绝对定位:脱标,利用边偏移指定准确位置
  4. 浮动:脱标,不能指定准确位置,让多个块级元素在一行显示

课堂练习:模拟老师的随堂案例完成**哈根达斯**案例(5 分钟)。

结构修改:

HTML
<!-- 修改.box-bd里面的li标签内容,添加一个hot图标 -->

<li>
     <!-- 添加hot小图片 -->
    <em>
        <img src="images/hot.png" alt="">
    </em>
    <img src="images/pic.png" alt="">
    <h4>
        Think PHP 5.0 博客系统实战项目演练
    </h4>
    <div class="info">
        <span>高级</span> • 1125人在学习
    </div>
</li>

样式修改:

CSS
.box-bd ul li {
    /* 子绝父相 */
    /* 父元素相对定位 */
    position: relative;
    float: left;
    width: 228px;
    height: 270px;
    background-color: #fff;
    margin-right: 15px;
    margin-bottom: 15px;

}
.box-bd ul li > img {
    width: 100%;
}
.box-bd ul li h4 {
    margin: 20px 20px 20px 25px;
    font-size: 14px;
    color: #050505;
    font-weight: 400;
}
.box-bd ul li em {
     /* 子元素绝对定位 */
    position: absolute;
    top: 4px;
    right: -4px;
}

3. 定位(position)的应用

3.1. 固定定位小技巧: 固定在版心左侧位置。

小算法:

1.让固定定位的盒子 left: 50%. 走到浏览器可视区(也可以看做版心) 的一半位置。

2.让固定定位的盒子 margin-left: 版心宽度的一半距离。 多走 版心宽度的一半位置

就可以让固定定位的盒子**贴着版心右侧对齐**了。

案例效果:

1571389241926

HTML
<style>
        .w {
            width: 800px;
            height: 1400px;
            background-color: pink;
            margin: 0 auto;
        }
        .fixed {
            position: fixed;
            /* 1. 走浏览器宽度的一半 */
            left: 50%;
            /* 2. 利用margin 走版心盒子宽度的一半距离 */
            margin-left: 405px;
            width: 50px;
            height: 150px;
            background-color: skyblue;
        }
    </style>
</head>
<body>
    <div class="fixed"></div>
    <div class="w">版心盒子 800像素</div>

</body>

3.2. 堆叠顺序(z-index)

  • 在使用**定位**布局时,可能会**出现盒子重叠的情况**。此时,可以使用 z-index 来控制盒子的前后次序 (z轴)

  • 语法:

Text Only
1
2
3
选择器 { 
  z-index: 1; 
}
  • z-index 的特性如下:

  • 属性值正整数负整数**或 **0,默认值是 0,数值越大,盒子越靠上;

  • 如果**属性值相同**,则按照书写顺序,后来居上
  • 数字后面**不能加单位**。

注意z-index 只能应用于**相对定位**、绝对定位**和**固定定位**的元素,其他**标准流、**浮动**和**静态定位**无效。

  • 应用 z-index 层叠等级属性可以**调整盒子的堆叠顺序**。如下图所示:

zindex示意图

案例演示:堆叠顺序。

4. 定位(position)的拓展

4.1 绝对定位的盒子居中

注意:加了**绝对定位/固定定位的盒子**不能通过设置 margin: auto 设置**水平居中**。

但是可以通过以下计算方法实现水平和垂直居中,可以按照下图的方法:

绝对定位水平居中

  1. left: 50%;:让**盒子的左侧**移动到**父级元素的水平中心位置**;
  2. margin-left: -100px;:让盒子**向左**移动**自身宽度的一半**。

盒子居中定位示意图

绝对定位居中示意图

4.2 定位特殊特性

绝对定位和固定定位也和浮动类似。

1.行内元素添加绝对或者固定定位,可以直接设置高度和宽度。

2.块级元素添加绝对或者固定定位,如果不给宽度或者高度,默认大小是内容的大小。

前面我们讲过, display 是 显示模式, 可以改变显示模式有以下方式:

  • 可以用inline-block 转换为行内块
  • 可以用浮动 float 默认转换为行内块(类似,并不完全一样,因为浮动是脱标的)
  • 绝对定位和固定定位也和浮动类似, 默认转换的特性 转换为行内块。

所以说, 一个行内的盒子,如果加了**浮动**、固定定位**和**绝对定位,不用转换,就可以给这个盒子直接设置宽度和高度等。

4.3 脱标的盒子不会触发外边距塌陷

浮动元素、**绝对定位(固定定位)**元素的都不会触发外边距合并的问题。 (我们以前是用padding border overflow解决的)

也就是说,我们给盒子改为了浮动或者定位,就不会有垂直**外边距合并的问题**了。

4.4 绝对定位(固定定位)会完全压住盒子

浮动元素不同,只会压住它下面标准流的盒子,但是不会压住下面标准流盒子里面的文字(图片)

但是绝对定位(固定定位) 会压住下面标准流所有的内容。

浮动之所以不会压住文字,因为浮动产生的目的最初是为了做文字环绕效果的。 文字会围绕浮动元素

5. 综合案例 - 淘宝轮播图(重点)

5.1 效果图

5.2 布局分析

1571397019689

5.3 步骤

  1. 大盒子我们类名为: tb-promo 淘宝广告

  2. 里面先放一张图片。

  3. 左右两个按钮 用链接就好了。 左箭头 prev 右箭头 next

​ 左按钮样式(border-radius:左上,右上,右下,左下),

​ 右按钮定位,提取左右按钮共同的样式代码(并集选择器)

  1. 底侧小圆点ul 继续做。 类名为 promo-nav

​ 中间长方形椭圆 ul的定位(水平居中,离底部15px)

​ 长方形需要五个小圆点,ul无序列表,li浮动,椭圆中小圆点的样式

5.4 知识点:圆角矩形设置4个角

圆角矩形可以为4个角分别设置圆度, 但是是有顺序的

Text Only
1
2
3
4
border-top-left-radius:20px;
border-top-right-radius:20px;
border-bottom-right-radius:20px;
border-bottom-left-radius:20px;
  • 如果4个角,数值相同
CSS
border-radius: 15px;
  • 里面数值不同,我们也可以按照简写的形式,具体格式如下:
CSS
border-radius: 左上角 右上角  右下角  左下角;

还是遵循的顺时针。

5.5 代码参考

HTML
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>淘宝轮播图做法</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        li {
            list-style: none;
        }

        .tb-promo {
            position: relative;
            width: 520px;
            height: 280px;
            background-color: pink;
            margin: 100px auto;
        }

        .tb-promo img {
            width: 520px;
            height: 280px;
        }

        /* 并集选择器可以集体声明相同的样式 */
        .prev,
        .next {
            position: absolute;
            /* 绝对定位的盒子垂直居中 */
            top: 50%;
            margin-top: -15px;
            /* 加了绝对定位的盒子可以直接设置高度和宽度 */
            width: 20px;
            height: 30px;
            background: rgba(0, 0, 0, .3);
            text-align: center;
            line-height: 30px;
            color: #fff;
            text-decoration: none;
        }

        .prev {
            left: 0;
            /* border-radius: 15px; */
            border-top-right-radius: 15px;
            border-bottom-right-radius: 15px;
        }

        .next {
            /* 如果一个盒子既有left属性也有right属性,则默认会执行 left属性 同理  top  bottom  会执行 top */
            right: 0;
            /* border-radius: 15px; */
            border-top-left-radius: 15px;
            border-bottom-left-radius: 15px;
        }
        .promo-nav {
            position: absolute;
            bottom: 15px;
            left: 50%;
            margin-left: -35px;
            width: 70px;
            height: 13px;
            /* background-color: pink; */
            background: rgba(255,255,255, .3);
            border-radius: 7px;
        }
        .promo-nav li {
            float: left;
            width: 8px;
            height: 8px;
            background-color: #fff;
            border-radius: 50%;
            margin: 3px;
        }
        /* 不要忘记选择器权重的问题 */
       .promo-nav .selected {
            background-color: #ff5000;
        }
    </style>
</head>

<body>
    <div class="tb-promo">
        <img src="images/tb.jpg" alt="">
        <!-- 左侧按钮箭头 -->
        <a href="#" class="prev"> &lt; </a>
        <!-- 右侧按钮箭头 -->
        <a href="#" class="next"> &gt; </a>
        <!-- 小圆点 -->
        <ul class="promo-nav">
            <li class="selected"></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>
    </div>
</body>

</html>

6. 网页布局总结

通过盒子模型,清楚知道大部分html标签是一个盒子。

通过CSS浮动、定位 可以让每个盒子排列成为网页。

一个完整的网页,是标准流、浮动、定位一起完成布局的,每个都有自己的专门用法。

6.1. 标准流

可以让盒子上下排列或者左右排列,垂直的块级盒子显示就用标准流布局

6.2. 浮动

可以让多个块级元素一行显示或者左右对齐盒子,多个块级盒子水平显示就用浮动布局

6.3. 定位

定位最大的特点是有层叠的概念,就是可以让多个盒子前后叠压来显示。如果元素自由在某个盒子内移动就用定位布局。

7. 元素的显示与隐藏

  • 目的(本质)

​ 让一个元素在页面中消失或者显示出来

  • 场景

​ 类似网站广告,当我们点击关闭就不见了,但是我们重新刷新页面,会重新出现!

7.1. display 显示(重点)

  • display 设置或检索对象是否及如何显示。
Text Only
1
2
3
display: none 隐藏对象

display:block 除了转换为块级元素之外,同时还有显示元素的意思。
  • 特点: display 隐藏元素后,**不再占**有原来的位置。

  • 后面应用及其广泛,搭配 JS 可以做很多的网页特效。实际开发场景:

配合后面js做特效,比如下拉菜单,原先没有,鼠标经过,显示下拉菜单, 应用极为广泛

7.2. visibility 可见性 (了解)

  • visibility 属性用于指定一个元素应可见还是隐藏。
Text Only
1
2
3
visibility:visible ;  元素可视

visibility:hidden;    元素隐藏
  • 特点:visibility 隐藏元素后,继续占有原来的位置。(停职留薪)

  • 如果隐藏元素想要原来位置, 就用 visibility:hidden

  • 如果隐藏元素不想要原来位置, 就用 display:none (用处更多 重点)

7.3. overflow 溢出(重点)

  • overflow 属性指定了如果内容溢出一个元素的框(超过其指定高度及宽度) 时,会发生什么。
属性值 描述
visible 不剪切内容也不添加滚动条
hidden 不显示超过对象尺寸的内容,超出的部分隐藏掉
scroll 不管超出内容否,总是显示滚动条
auto 超出自动显示滚动条,不超出不显示滚动条
  • 一般情况下,我们都不想让溢出的内容显示出来,因为溢出的部分会影响布局。
  • 但是如果有定位的盒子, 请慎用overflow:hidden 因为它会隐藏多余的部分。

  • 实际开发场景:

  • 清除浮动

  • 隐藏超出内容,隐藏掉, 不允许内容超过父盒子。

7.4. 显示与隐藏总结

属性 区别 用途
display 显示 (重点) 隐藏对象,不保留位置 配合后面js做特效,比如下拉菜单,原先没有,鼠标经过,显示下拉菜单, 应用极为广泛
visibility 可见性 (了解) 隐藏对象,保留位置 使用较少
overflow 溢出(重点) 只是隐藏超出大小的部分 1. 可以清除浮动 2. 保证盒子里面的内容不会超出该盒子范围

8 综合案例:土豆网鼠标经过显示遮罩

8.1. 效果图

8.2. 案例目标

1.练习元素的显示与隐藏

2.练习元素的定位

8.3. 核心原理

原先半透明的黑色遮罩看不见, 鼠标经过 大盒子,就显示出来。

遮罩的盒子不占有位置, 就需要用绝对定位 和 display 配合使用。

8.4. 代码参考

HTML
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>仿土豆网显示隐藏遮罩案例</title>
    <style>
        .tudou {
            position: relative;
            width: 444px;
            height: 320px;
            background-color: pink;
            margin: 30px auto;
        }

        .tudou img {
            width: 100%;
            height: 100%;
        }

        .mask {
            /* 隐藏遮罩层 */
            display: none;
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, .4) url(images/arr.png) no-repeat center;
        }

        /* 当我们鼠标经过了 土豆这个盒子,就让里面遮罩层显示出来 */
        .tudou:hover .mask {
            /* 而是显示元素 */
            display: block;
        }
    </style>
</head>

<body>
    <div class="tudou">
        <div class="mask"></div>
        <img src="images/tudou.jpg" alt="">
    </div>
    <div class="tudou">
        <div class="mask"></div>
        <img src="images/tudou.jpg" alt="">
    </div>
    <div class="tudou">
        <div class="mask"></div>
        <img src="images/tudou.jpg" alt="">
    </div>
    <div class="tudou">
        <div class="mask"></div>
        <img src="images/tudou.jpg" alt="">
    </div>
</body>

</html>

CSS 的复合选择器

什么是复合选择器

在 CSS 中,可以根据选择器的类型把选择器分为基础选择器和复合选择器,复合选择器是建立在基础选择器之上,对基本选择器进行组合形成的。

  • 复合选择器可以更准确、更高效的选择目标元素(标签)
  • 复合选择器是由两个或多个基础选择器,通过不同的方式组合而成的
  • 常用的复合选择器包括:后代选择器、子选择器、并集选择器、伪类选择器等等

后代选择器(重要)

后代选择器又称为包含选择器,可以选择父元素里面子元素。其写法就是把外层标签写在前面,内层标签写在后面,中间用空格分隔。当标签发生嵌套时,内层标签就成为外层标签的后代。

Bash
元素1 元素2 { 样式声明 }

上述语法表示选择元素 1 里面的所有元素 2 (后代元素)。

例如:

Bash
ul li { 样式声明 } /* 选择 ul 里面所有的 li标签元素 */
  • 元素1 和 元素2 中间用空格隔开
  • 元素1 是父级,元素2 是子级,最终选择的是元素2
  • 元素2 可以是儿子,也可以是孙子等,只要是元素1 的后代即可
  • 元素1 和 元素2 可以是任意基础选择器

子选择器(重要)

子元素选择器(子选择器)只能选择作为某元素的最近一级子元素。简单理解就是选亲儿子元素.

语法:

Bash
元素1 > 元素2 { 样式声明 }

上述语法表示选择元素1 里面的所有直接后代(子元素) 元素2。

例如:

Bash
div > p { 样式声明 } /* 选择 div 里面所有最近一级 p 标签元素 */
  • 元素1 和 元素2 中间用 大于号 隔开
  • 元素1 是父级,元素2 是子级,最终选择的是元素2
  • 元素2 必须是亲儿子,其孙子、重孙之类都不归他管. 你也可以叫他 亲儿子选择器

并集选择器 (重要)

并集选择器是各选择器通过英文逗号(,)连接而成,任何形式的选择器都可以作为并集选择器的一部分。

例如:

Bash
ul,div { 样式声明 } /* 选择 ul  div标签元素 */
  • 元素1 和 元素2 中间用逗号隔开
  • 逗号可以理解为和的意思
  • 并集选择器通常用于集体声明

伪类选择器

伪类选择器书写最大的特点是用冒号(:)表示,比如 :hover 、 :first-child 。

因为伪类选择器很多,比如有链接伪类、结构伪类等,所以这里先给大家讲解常用的链接伪类选择器。

链接伪类选择器

链接伪类选择器注意事项
  1. 为了确保生效,请按照 LVHA 的循顺序声明 :link-:visited-:hover-:active。
  2. 记忆法:love hate 或者 lv 包包 hao 。
  3. 因为 a 链接在浏览器中具有默认样式,所以我们实际工作中都需要给链接单独指定样式。
链接伪类选择器

链接伪类选择器实际工作开发中的写法:

HTML
1
2
3
4
5
6
7
8
/* a 是标签选择器 所有的链接 */
a {
color: gray;
}
/* :hover 是链接伪类选择器 鼠标经过 */
a:hover {
color: red; /* 鼠标经过的时候,由原来的 灰色 变成了红色 */
}

:focus 伪类选择器

:focus 伪类选择器用于选取获得焦点的表单元素。 焦点就是光标,一般情况 类表单元素才能获取,因此这个选择器也主要针对于表单元素来说。

HTML
1
2
3
input:focus {
background-color:yellow;
}

复合选择器总结

选择器 作用 特征 使用情况 隔开符号及用法
后代选择器 用来选择后代元素 可以是子孙后代 较多 符号是空格.nav a
子代选择器 选择最近一级元素 只能选亲儿子 较少 符号是大于 .nav >p
并集选择器 选择某些相同样式的元素 可以用于集体申明 较多 符号是逗号 .nav,.header
链接伪类选择器 选择不同状态的链接 选择不同状态的链接 较多 重点记住a{}和a:hover 实际开发的写法
:focus选择器 选择获得光标的表单 选择获得光标的表单 较少 input:focus 记住这个写法

ftp上传下载文件

libcurl从ftp上获取文件列表信息,包含中文

C++
inline std::wstring to_wstring(std::string const& str)
{
    std::wstring ustr;
    int const length = MultiByteToWideChar(
        CP_UTF8, 0,
        str.c_str(), static_cast<int>(str.length()),
        nullptr, 0);
    if (length <= 0)
        return ustr;
    ustr.resize(static_cast<size_t>(length));
    int const result = MultiByteToWideChar(
        CP_UTF8, 0,
        str.c_str(), static_cast<int>(str.length()),
        const_cast<wchar_t*>(ustr.data()), length);
    if (result != length)
        ustr.clear();
    return ustr;
}

std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8Converter;

size_t WriteCallback(char* contents, size_t size, size_t nmemb, std::string* userp)
{
    size_t totalSize = size * nmemb;
    userp->append(contents, totalSize);
    return totalSize;
}
bool GetFtpFileList(const char* ftpUrl, const char* username, const char* password, CString& strResult)
{
    CURL* curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl)
    {
        std::string response;

        struct curl_slist* headers = NULL;
        headers = curl_slist_append(headers, "Accept: */*");
        string strUserName = username;
        string strPassWord = password;
        curl_easy_setopt(curl, CURLOPT_URL, ftpUrl);
        curl_easy_setopt(curl, CURLOPT_USERPWD, (strUserName + ":" + strPassWord).c_str());  // 密码和用户名以":"连接,如:"username:password"
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_FTP_USE_EPRT, 1L);  // 使用更安全的EPR命令(如果支持)
        curl_easy_setopt(curl, CURLOPT_FTPLISTONLY, 1L);   // 只列出文件,不下载

        res = curl_easy_perform(curl);

        if (res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }
        else
        {
            // 尝试将字符串转换为宽字符串以便正确显示中文等非ASCII字符
            std::wstring wideResponse = utf8Converter.from_bytes(response);
            std::wcout << L"FTP directory listing:\n";
            std::wcout << wideResponse.c_str() << std::endl;
            strResult = wideResponse.c_str();
        }

        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();

    return (res == CURLE_OK);
}
int GetFtpDirectoryList(const char* ftpUrl, const char* username, const char* password, const char* strRemotePath, vector<CString>& fileList)
{
    CURL* curl;
    CURLcode res;
    CString strRemote = strRemotePath;
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl)
    {
        std::string response;

        curl_slist* headers = NULL;
        headers = curl_slist_append(headers, "Accept: */*");
        string strUserName = username;
        string strPassWord = password;
        curl_easy_setopt(curl, CURLOPT_URL, ftpUrl);
        curl_easy_setopt(curl, CURLOPT_USERPWD, (strUserName + ":" + strPassWord).c_str());  // 密码和用户名以":"连接,如:"username:password"
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_FTP_USE_EPRT, 1L);  // 使用更安全的EPR命令(如果支持)
        curl_easy_setopt(curl, CURLOPT_DIRLISTONLY, 1L);   // 只列出目录内容

        res = curl_easy_perform(curl);

        if (res == CURLE_OK)
        {
            // 解析响应字符串,提取文件和子目录名
            // 这里假设FTP服务器返回的是NLST格式,每行一个条目
            std::wstring wideResponse = utf8Converter.from_bytes(response);
            CString strResult = wideResponse.c_str();
            vector<CString> dirVec;
            dirVec.clear();
            int nFind = strResult.Replace(_T("\r\n"), _T("\r\n"));
            if (nFind > 0)
            {
                // 使用CString::Find()函数找到第一个\r\n的位置
                int pos = strResult.Find(_T("\r\n"));
                // 循环遍历所有\r\n分隔符,并将字符串拆分成一个一个的
                while (pos != -1)
                {
                    // 获取当前分隔符之前的子串
                    CString subStr = strResult.Left(pos);
                    if ((subStr.CompareNoCase(_T(".")) == 0)
                        ||(subStr.CompareNoCase(_T("..")) == 0))
                    {
                        continue;
                    }

                    if (subStr.GetAt(subStr.GetLength() - 1) == _T('/'))
                    {
                        dirVec.push_back(strRemote + subStr);
                    }
                    strRemote.Replace(_T("/"), _T("\\"));

                    fileList.push_back(strRemote + subStr);
                    // 将字符串指针移动到下一个分隔符的位置
                    strResult = strResult.Mid(pos + 2);
                    // 重新获取分隔符的位置
                    pos = strResult.Find(_T("\r\n"));
                }
                // 处理最后一个子串

                if (!strResult.IsEmpty())
                {
                    if (strResult.GetAt(strResult.GetLength() - 1) == _T('/'))
                    {
                        dirVec.push_back(strRemote + strResult);
                    }
                    fileList.push_back(strRemote + strResult);
                }
            }
            else
            {
                fileList.push_back(strRemote + strResult);
            }
            // 对于每个子目录,进行递归调用

            for (auto entry : dirVec)
            {
                string strFtp = ftpUrl;
                string strEntry = (LPCSTR)(CStringA)(entry);
                strEntry += +"/";
                string subDirUrl = strFtp + strEntry;
                GetFtpDirectoryList(subDirUrl.c_str(), username, password, strEntry.c_str(), fileList);
            }
        }
        else
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }

        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();

    return (res == CURLE_OK);
}

CPP

具体调用函数为:

C++
static void ADSKMyGroupTEST() {

        //const char* ftpServer = "ftp://127.0.0.1:21/";
        //const char* ftpUsername = "ftp";
        //const char* ftpPassword = "ftp";

        CString strFtpSerVer = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPSERVER);
        CString ftpServer = _T("ftp://") + strFtpSerVer;
        CString ftpUsername = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPUSER);
        CString ftpPassword = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPPASSWD);
        CString strLocal = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPLOCALPATH);
        std::vector<CString> allFileList;

        GetFtpDirectoryList((LPCSTR)(LPCTSTR)ftpServer, (LPCSTR)(LPCTSTR)ftpUsername, (LPCSTR)(LPCTSTR)ftpPassword, "", allFileList);

        FtpManage ftp;
        ftp.ConnectFtp();
        for (const auto& item : allFileList)
        {
            CString localPath = strLocal + item;  // 设置本地存储路径

            if (localPath.Find(_T(".")) < 0)  // 如果是目录
            {
                ::CreateDirectory(localPath, NULL);
            }
            else  // 如果是文件
            {
                CString ftpFilePath = _T("/") + item;
                string outputfile = (LPCSTR)(CStringA)(localPath);
                ftp.GetFileFromFtp(ftpFilePath, localPath);
            }
        }
        ftp.CloseFtpConnect();
    }

CPP

从ftp下载文件

ftpmanage.h

C++
#pragma once
#include <afxinet.h>
#include <vector>
using namespace std;

//对 ftp文件进行下载及相关的操作类
class FtpManage
{
public:
    FtpManage();
    ~FtpManage();
private:
    void InitFtpSetting();

    CString m_strFTPServer;//ftp服务器
    CString m_strFTPPassive;//passive标记
    CString m_strFTPRoot;//ftp根目录
    CString m_strFTPUser;//ftp账号
    CString m_strFTPPassWd;//ftp密码

    CInternetSession* m_pInetSession;
    CFtpConnection* m_pFtpConnection;
    vector<CString> getSplitStringVector(CString strFtpPath, LPCTSTR param2 = _T("/"));
public:


    int ConnectFtp();

    //从ftp下载文件
    int GetFileFromFtp(/*CString strFtpPath, */CString strFtpFileName, CString strDwgSavePath);

    //上传文件到ftp
    int PutFileToFtp(CString strLocalFilePath, CString strFtpPath);
    int PutFileToFtpEx(CString strLocalFilePath, CString strFtpPath, CString strFtpFileName);

    //分级设置ftp dir 
    int SetCurrentFtpDir(CString strFtpPath);

    //关闭ftp连接
    int CloseFtpConnect();
    //获取ftp目录中的所有文件//获取中文文件名乱码,不可用
    vector<CString> getAllFileFromFtpServer();
};

CPP

ftpmanage.cpp

C++
#include "stdafx.h"
#include "FtpManage.h"
#include <xlocbuf>
#include <codecvt>

FtpManage::FtpManage():m_pInetSession(NULL),m_pFtpConnection(NULL)
{
    InitFtpSetting();
}

FtpManage::~FtpManage()
{

}

void FtpManage::InitFtpSetting()
{
    m_strFTPServer = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPSERVER);
    m_strFTPPassive = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPPASSIVE);
    m_strFTPRoot = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPROOT);
    m_strFTPUser = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPUSER);
    m_strFTPPassWd = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPPASSWD);
}

std::vector<CString> FtpManage::getSplitStringVector(CString strFtpPath, LPCTSTR param2 /*= _T("/")*/)
{
    vector<CString> retVec;
    retVec.clear();
    int nFind = strFtpPath.Replace(_T("/"), _T("/"));
    if (nFind <= 0)
    {
        retVec.push_back(_T("/"));
        return retVec;
    }
    for (int i=0; i<=nFind; i++)
    {
        retVec.push_back(CUtility::SubString(strFtpPath, _T("/"), i) + _T("/"));
    }
    return retVec;
}

int FtpManage::GetFileFromFtp(/*CString strFtpPath, */CString strFtpFileName, CString strDwgSavePath)
{
    //if (SetCurrentFtpDir(strFtpPath)<0)
    //{
    //  return -1;
    //}
    CFtpFileFind findFile(m_pFtpConnection);
    if (findFile.FindFile(strFtpFileName, INTERNET_FLAG_DONT_CACHE))
    {
        if (!m_pFtpConnection->GetFile(strFtpFileName,strDwgSavePath, FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_BINARY, 1))
        {
            DWORD dw = GetLastError();
            CString sError;
            AfxMessageBox(_T("ftp getfile error :%d"), dw);
            return -3;
        }
    }
    else
    {
        return -2;
    }

    return 0;
}

int FtpManage::ConnectFtp()
{
    CWaitCursor wait;

    CString m_sDomainName(m_strFTPServer);
    CString m_ftpUser(m_strFTPUser);
    CString m_ftpPassword(m_strFTPPassWd);

    m_pFtpConnection = NULL;
    m_pInetSession = new CInternetSession(_T("ESAPP"), 1, PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, INTERNET_FLAG_DONT_CACHE);
    if (!m_pInetSession)
    {
        return -1;
    }

    CString strFtpSite = m_sDomainName;
    CString strServerName;
    CString strObject;
    INTERNET_PORT nPort;
    DWORD dwServiceType;

    //检查URL是否正确
    if (!AfxParseURL(strFtpSite, dwServiceType, strServerName, strObject, nPort) || dwServiceType == AFX_INET_SERVICE_UNK)
    {
        CString strFtpURL = _T("ftp://");
        strFtpURL += strFtpSite;

        if (!AfxParseURL(strFtpURL, dwServiceType, strServerName, strObject, nPort))
        {
            return -2;
        }
    }

    if ((dwServiceType == INTERNET_SERVICE_FTP) && !strServerName.IsEmpty())
    {
        try
        {
            //AfxMessageBox(strServerName + _T("\r") + m_ftpUser + _T("\r") + m_ftpPassword);
            if (m_strFTPPassive == _T("TRUE"))
            {
                m_pFtpConnection = m_pInetSession->GetFtpConnection(strServerName, m_ftpUser, m_ftpPassword, nPort, TRUE);
            }
            else
            {
                m_pFtpConnection = m_pInetSession->GetFtpConnection(strServerName, m_ftpUser, m_ftpPassword, nPort, FALSE);
            }
        }
        catch (CInternetException* pEx)
        {
            CString strInteError = _T("");
            TCHAR szErr[1024];
            if (pEx->GetErrorMessage(szErr, 1024))
            {
                strInteError.Format(_T("%s"), szErr);
                pEx->Delete();
            }
            AfxMessageBox(strInteError);
            return -3;
        }
    }
    return 0;
}

int FtpManage::PutFileToFtp(CString strLocalFilePath, CString strFtpPath)
{
    CString sFileName;
    int nFind = strLocalFilePath.ReverseFind(_T('\\'));
    sFileName = strLocalFilePath.Mid(nFind+1);

    return PutFileToFtpEx(strLocalFilePath, strFtpPath, sFileName);
}

int FtpManage::PutFileToFtpEx(CString strLocalFilePath, CString strFtpPath, CString strFtpFileName)
{
    int nRes = SetCurrentFtpDir(strFtpPath);
    if (nRes!=0)
    {
        return -1;
    }
    CFtpFileFind findFile(m_pFtpConnection);
    if (findFile.FindFile(strFtpFileName,INTERNET_FLAG_DONT_CACHE))
    {
        m_pFtpConnection->Remove(strFtpFileName);
    }
    if (!m_pFtpConnection->PutFile(strLocalFilePath,strFtpFileName))
    {
        DWORD dw = GetLastError();
        int nError = (int)dw;
        CString strInterError;
        strInterError.Format(_T("%d"), nError);
        AfxMessageBox(strInterError);
        return -2;
    }
    return 0;
}

int FtpManage::SetCurrentFtpDir(CString strFtpPath)
{
    if (m_pFtpConnection==NULL)
    {
        return -1;
    }
    //会存在多级 然后一次设置下去 如果失败了 就返回问题
    strFtpPath.Replace(_T("\\"), _T("/"));

    vector<CString> vecCatalogue;
    //gMyString.Split(strFtpPath, _T("/"), vecCatalogue);
    vecCatalogue = getSplitStringVector(strFtpPath, _T("/"));
    for (int i=0;i<vecCatalogue.size();i++)
    {
        CString sTempCatalogue = vecCatalogue[i];
        if (sTempCatalogue==_T(""))
        {
            continue;
        }
        if (!m_pFtpConnection->SetCurrentDirectory(sTempCatalogue))
        {
            if (!m_pFtpConnection->CreateDirectory(sTempCatalogue))
            {
                return -2;
            }
            if (!m_pFtpConnection->SetCurrentDirectory(sTempCatalogue))
            {
                return -3;
            }
        }
    }
    return 0;
}

int FtpManage::CloseFtpConnect()
{
    if (m_pFtpConnection!=NULL)
    {
        m_pFtpConnection->Close();
        delete m_pFtpConnection;
        m_pFtpConnection = NULL;
    }
    if (m_pInetSession!=NULL)
    {
        m_pInetSession->Close();
        delete m_pInetSession;
        m_pInetSession = NULL;
    }
    return 0;
}
wchar_t * ANSIToUnicode(const char* str)
{
    int textlen;
    wchar_t * result;
    textlen = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
    result = (wchar_t *)malloc((textlen + 1) * sizeof(wchar_t));
    memset(result, 0, (textlen + 1) * sizeof(wchar_t));
    MultiByteToWideChar(CP_ACP, 0, str, -1, (LPWSTR)result, textlen);
    return result;
}

//获取中文文件名乱码
std::vector<CString> FtpManage::getAllFileFromFtpServer()
{
    vector<CString> tmpVec;
    tmpVec.clear();
    if (SetCurrentFtpDir(m_strFTPServer) < 0)
    {
        return tmpVec;
    }
    try
    {
        // use a file find object to enumerate files
        CFtpFileFind findFile(m_pFtpConnection);
        CString strName, strDirectory;
        m_pFtpConnection->GetCurrentDirectory(strDirectory);
        // start looping
        BOOL bWorking = findFile.FindFile(_T("*"));

        //while (bWorking)
        //{
        //  bWorking = findFile.FindNextFile();
        //  tmpVec.push_back(findFile.GetFileURL());
        //  //_tprintf_s(_T("%s\n"), (LPCTSTR)findFile.GetFileURL());
        //}

        BOOL bFind = findFile.FindFile(_T("/"), INTERNET_FLAG_EXISTING_CONNECT);
        bool flag = false;
        while (bFind)
        {
            bFind = findFile.FindNextFile();

            if (findFile.IsDots())
            {
                continue;
            }
            CString remoteFileName = findFile.GetFileName();
            // 转换为 UTF-8 编码
            std::string fileNameUtf8 = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(findFile.GetFileName());

            CString strName = fileNameUtf8.c_str();

    /*      CString remoteFilePath = remoteDir + remoteFileName;
            CString localFilePath = localDir + remoteFileName;*/
            //flag = 1为获取目录下的子文件,flag = 0为获取当前文件夹下的所有子文件。
            if (flag)
            {
                if (findFile.IsDirectory())
                {
                    // 如果是目录,递归下载其中的文件和子目录
                    //CreateDirectory(localFilePath, nullptr);

                    strDirectory = strDirectory + _T("/") + strName;
                    tmpVec.push_back(strName);
                }
            }
            else
            {
                tmpVec.push_back(strName);
            }
        }

        findFile.Close();
    }
    catch (CInternetException* pEx)
    {
        TCHAR sz[1024];
        pEx->GetErrorMessage(sz, 1024);
        _tprintf_s(_T("ERROR!  %s\n"), sz);
        pEx->Delete();
    }
    return tmpVec;
}