CSS实现前端小组件随笔

一.CSS+JS实现打字机效果

1.1实现效果

1.2HTML部分

<span class="bottom-text"></span>

1.3CSS部分

.bottom-text {
    font-fanmily: "fangsong";
    display:inline-block;
    position:relative;
    font-size:20px;
    height:20px;
    inline-height:20px;
    color:white;
}

.bottom-text::after {
    content:"";
    position:absolute;
    right:-10px;
    top:5px;
    height:20px;
    width:2px;
    background-color:#fff;
    //指针动画效果
    animation: san 0.5s steps(1) infinite;
}
@keyframes san{
    0%,100% {
        background-color:#fff;
       }
       50% {
            background-color:transparent;
       }
}

1.4JS部分

    <script>
        //页面底部打字机效果
        const text = document.querySelector(".bottom-text");
        //需要轮替的文本内容列表
        const txt = ["你不比任何人差。","答案在风中飘荡。"];
        //当前文本内容的字符下标
        var crIndex = 0;
        //文本内容列表的下标
        var txtIndex = 0;
        //用来确定是打字还是删字,true是打字,false是删字
        var switchMode = true;
        setInterval(function(){
            if(switchMode) {
                text.innerHTML = txt[txtIndex].slice(0,++crIndex);
            }
            else {
                text.innerHTML = txt[txtIndex].slice(0,crIndex);
                crIndex--;
            }

            //+3是为了打完全部字后停留一段时间,更好的交互效果
            if(crIndex == txt[txtIndex].length+3) {
                switchMode = false;
            }
            //当前文本内容被删光后,进行下一个文本内容的打字
            else if (crIndex < 0) {
                crIndex = 0;
                switchMode = true;
                txtIndex++;
                if(txtIndex >= txt.length){
                    txtIndex = 0;
                }
            }
        //200ms表示打字的快慢,越低打字越快
        },200);
    </script>

二.Layui+JS实现多图片上传预览图+删除

2.1实现效果(悬浮效果):

2.2实现效果(删除效果)

2.3实现效果(预览效果)

2.4实现思路

  • 首先在form表单中,创建一个隐藏的<input>用来临时存放上传图片的路径,图片与图片之间,使用“;”隔开,例如:“xxx.png;xxx.png;xxx.png
  • 每添加一张图片,动态给图片添加悬浮事件,当悬浮时,悬浮窗盒子显示(即黑色背景有删除、预览按钮的盒子),可以使用“Layui库中的类属性layui-hide”实现,当悬浮时删除该属性,鼠标移出后再次添加这个类属性
  • 添加的图片临时存储在服务器的“tempImg”文件夹中,当点击删除按钮后,首先删除图片DOM节点,其次在服务器中删除该图片,可以创建一个下标索引(indexImg)和图片映射(mapImg)来反应图片下表与图片路径的关系。

2.5HTML部分

<div class="layui-form-item">
    <button type="button" class="layui-btn" id="uploadImg" style="margin-left:2.0rem;">
        <i class="layui-icon layui-icon-upload"></i> 多图片上传
    </button>
    <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top:11px;">
        预览图:
    <div class="layui-upload-list" id="upload-img-preview"></div>
    <input name="imglist" type="text" id="imgInput" style="display:none;">
    </blockquote>
</div>

2.6CSS部分

<style>
    .imgBlock {
        width:180px;
        height:180px;
        margin:0 0.5rem 0.5rem 0;
        display:inline-block;
    }
    img {
        width:100%;
        height:100%;
    }
    .imgHoverBlock {
        display: flex;
        justify-content: center;
        align-items: center;
        background: rgba(59, 60, 61, 0.6);
        opacity:0.7;
        width:100%;
    }
    .iSize {
        cursor:pointer;
        font-size:20px;
        color:black;
    }
    .iSize:hover {
        transform:scale(1.5);
    }
</style>

2.7JavaScript部分

        layui.use(function(){
            const upload = layui.upload;
            upload.render({
                elem:"#uploadImg",
                url:"/main/blog/moreimgUpload",
                multiple:true,
                done:function(res){
                    //拼接预览图片
                    $("#upload-img-preview").append(
                        "<div id=" + "img" + imgIndex + " class='imgBlock'>" +
                        "<img src=" + "/" + res["url"] + ">" +
                        "<div class='imgHoverBlock layui-hide' style='height:100%;'>" +
                        "<div class='iSize'><i class='layui-icon layui-icon-close i-clickDelete'></i><i class='layui-icon layui-icon-eye i-clickView'></i></div>" +
                        "</div>" + "</div>"
                    );
                    //#imgInput是一个隐藏输入框,用来临时存储图片路径
                    targetUrl = res["url"].split("/").pop();
                    $("#imgInput").val($("#imgInput").val()+targetUrl+";");
                    //为预览图添加悬浮界面
                    $("#img"+imgIndex).hover(function(){
                        $(this).find("div:first").removeClass("layui-hide");
                        $(this).find("img:first").addClass("layui-hide");
                    },function(){
                        $(this).find("div:first").addClass("layui-hide");
                        $(this).find("img:first").removeClass("layui-hide");
                    });
                    //将图片下标以及路径添加到映射中
                    imgMap["img"+imgIndex] = res["url"];
                    //为悬浮界面图标添加点击事件
                    $(".i-clickDelete").click(function(){
                        const targetDom = $(this).parent().parent().parent();
                        const targetDomId = targetDom.prop("id");
                        //imgInput临时图片路径数组
                        let tempImgInputList = $("#imgInput").val().split(";");
                        //console.log("原先的值是:",$("#imgInput").val());
                        //在服务器中删除对应的图片
                        $.post("/main/blog/moreimgUpload/temp/Delete",{"path":imgMap[targetDomId]});
                        //修改imgInput的值(用来临时存储所有图片路径的标签)
                        for(let i = 0;i<tempImgInputList.length;i++){
                            if(tempImgInputList[i] == imgMap[targetDomId].split("/").pop()){
                                tempImgInputList.splice(i,1);
                                break;
                            }
                        }
                        //拼接imgInput字符串
                        $("#imgInput").val(tempImgInputList.join(""));
                        //console.log("之后的值是:",$("#imgInput").val());
                        //删除映射对象中对应的值
                        delete imgMap[targetDomId];
                        //删除轮播图DOM节点
                        targetDom.remove();
                    });
                    $(".i-clickView").click(function(){
                        const targetDom = $(this).parent().parent().parent();
                        const targetDomId = targetDom.prop("id");
                        console.log(imgMap[targetDomId]);
                        layui.use(function(){
                            var layer = layui.layer;
                            layer.photos({
                                photos: {
                                "title": "Photos Demo",
                                "start": 0,
                                "data": [
                                    {
                                        "alt": "",
                                        "pid": 5,
                                        "src": "/" + imgMap[targetDomId],
                                    }
                                  ]
                                },
                                footer: false // 是否显示底部栏 --- 2.8.16+
                            });
                        });
                    });
                    //预览图下标更新
                    imgIndex++;
                }
            });
        });

2.8后端接口(Python版本,供参考)

#删除临时轮播图图片函数
@blog.route("/moreimgUpload/temp/Delete",methods=['POST'])
def imgTempDelete():
    imgPath = request.form.get("path")
    os.remove(imgPath)
    return {
        "status":1
    }

三.基于原生HTML+CSS实现的登录界面

3.1前言

该登录界面中使用了“layui”组件制作<input>标签以及前缀等,在使用时可以根据自己的需要,酌情删减,带有“layui-xx的地方使用了layui组件不使用layui组件库并不会对登录界面造成影响

3.2HTML部分

    <div class="container signal-active">
        <!-- 登录页面 -->
        <div class="container-form container--signup">
            <form class="form layui-form" id="form1" method="post">
                <h2 class="form-title">验证身份</h2>
                <div class="layui-form-item">
                    <div class="layui-input-wrap">
                        <div class="layui-input-prefix layui-input-split">
                            <i class="layui-icon layui-icon-username"></i>
                        </div>
                        <input type="password" name="username" lay-affix="eye" lay-verify="required" placeholder="邀请码" lay-reqtext="请填写邀请码" class="layui-input">
                    </div>
                </div>
                <div class="layui-form-item">
                    <input type="checkbox" name="remember" lay-skin="primary" title="保存登录">
                </div>
                <div class="layui-form-item">
                    <button class="btn" lay-submit lay-filtyer="login">确认</button>
                </div>
            </form>
        </div>
        <!-- 注册页面 -->
        <div class="container-form container--signin">
            <form class="form" id="form2">
                <h2 class="form-title">获取验证</h2>
            </form>
        </div>
        <!-- 移动覆盖层 -->
            <div class="container-overlay">
                <div class="overlay">
                    <div class="overlay-panel overlay--left">
                        <button class="btn" id="signIn">获取验证码</button>
                    </div>
                    <div class="overlay-panel overlay--right">
                        <button class="btn" id="signUp">验证身份</button>
                    </div>
                </div>
            </div>
    </div>

3.3JS部分

    <script>
        //获取按钮DOM节点
        const signInBtn = document.getElementById("signIn");
        const signUpBtn = document.getElementById("signUp");
        const container = document.querySelector(".container");
        //动态删减container容器的"signal-active"类属性,实现登录/注册两个窗口滑动
        signInBtn.addEventListener("click", () => {
            container.classList.remove("signal-active");
        });
        signUpBtn.addEventListener("click", () => {
            container.classList.add("signal-active");
        });
    </script>

3.4CSS部分

:root {
    /* 颜色 */
    --white: #e9e9e9;
    --gray: #333;
    --blue: #0367a6;
    --lightblue: #008997;
    /* 弧度 */
    --button-radius: 0.7rem;
    /* 大小 */
    --max-width: 758px;
    --max-height: 420px;
    font-size: 16px;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
    Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}

body {
    align-items: center;
    background-color: var(--white);
    background: url("/static/img/login_background.jpg");
    /* 决定背景图像的位置是在视口内固定,或者随着包含它的区块滚动。 */
    background-attachment: fixed;
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    display: grid;
    height: 100vh;
    place-items: center;
}

.form-title {
    font-weight: 300;
    margin: 0;
    margin-bottom: 1.25rem;
}

.link {
    color: var(--gray);
    font-size: 0.9rem;
    margin: 1.5rem 0;
    text-decoration: none;
}

.container {
    background-color: var(--white);
    border-radius: var(--button-radius);
    box-shadow: 0 0.9rem 1.7rem rgba(0, 0, 0, 0.25),
      0 0.7rem 0.7rem rgba(0, 0, 0, 0.22);
    height: var(--max-height);
    max-width: var(--max-width);
    overflow: hidden;
    position: relative;
    width: 100%;
}

.container-form {
    height: 100%;
    position: absolute;
    top: 0;
    transition: all 0.6s ease-in-out;
}

.container--signin {
    left: 0;
    width: 50%;
    z-index: 2;
}

.container.signal-active .container--signin {
    opacity: 0;
    transform: translateX(100%);
}

.container--signup {
    left: 0;
    opacity: 0;
    width: 50%;
    z-index: 1;
}

.container.signal-active .container--signup {
    animation: show 0.6s;
    opacity: 1;
    transform: translateX(100%);
    z-index: 5;
}

.container-overlay {
    height: 100%;
    left: 50%;
    overflow: hidden;
    position: absolute;
    top: 0;
    transition: transform 0.6s ease-in-out;
    width: 50%;
    z-index: 100;
}

.container.signal-active .container-overlay {
    transform: translateX(-100%);
}

.overlay {
    background-color: var(--lightblue);
    background: url("/static/img/login_overlay.jpg");
    background-attachment: fixed;
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    height: 100%;
    left: -100%;
    position: relative;
    transform: translateX(0);
    transition: transform 0.6s ease-in-out;
    width: 200%;
}

.container.signal-active .overlay {
    transform: translateX(50%);
}

.overlay-panel {
    align-items: center;
    display: flex;
    flex-direction: column;
    height: 100%;
    justify-content: center;
    position: absolute;
    text-align: center;
    top: 0;
    transform: translateX(0);
    transition: transform 0.6s ease-in-out;
    width: 50%;
}

.overlay--left {
    transform: translateX(-20%);
}

.container.signal-active .overlay--left {
    transform: translateX(0);
}

.overlay--right {
    right: 0;
    transform: translateX(0);
}

.container.signal-active .overlay--right {
    transform: translateX(20%);
}

.btn {
    background-color: var(--blue);
    background-image: linear-gradient(90deg, var(--blue) 0%, var(--lightblue) 74%);
    border-radius: 20px;
    border: 1px solid var(--blue);
    color: var(--white);
    cursor: pointer;
    font-size: 0.8rem;
    font-weight: bold;
    letter-spacing: 0.1rem;
    padding: 0.9rem 4rem;
    text-transform: uppercase;
    transition: transform 80ms ease-in;
}

.form>.btn {
    margin-top: 1.5rem;
}

.btn:active {
    transform: scale(0.95);
}

.btn:focus {
    outline: none;
}

.form {
    background-color: var(--white);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    padding: 0 3rem;
    height: 100%;
    text-align: center;
}

@keyframes show {
    0%,
    49.99% {
        opacity: 0;
        z-index: 1;
    }
    50%,
    100% {
        opacity: 1;
        z-index: 5;
    }
}

@media screen and (max-width:768px) {
    .form {
        padding:0.15rem;
    }
    .btn {
        padding:0.9rem 3rem;
    }
}

CSS中,我们对“form”类进行媒体查询适配不同大小的屏幕

3.5实现效果

3.5.1登录窗口

3.5.2注册窗口

四.前端常见问题

4.1怎么禁用<img>图片拖动

<img>的标签属性“draggable”设置为“false

<img src="xxx" draggable="false">

4.2怎么均等的缩放<img>图片

在响应式布局中,有时我们可能需要修改<img>的大小。

第一种方法,我们可以手动指定width/height,但是这种方法可能会导致图片扭曲

为此,我们可以使用“scale”来进行均等的缩放

例如,当屏幕宽度小于768px时,我们可以将该<img>标签缩小75%

@media screen and (max-width:768px) {
    img {
        transform:scale(75%);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是洋洋a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值