繁体   English   中英

没有 jQuery 的 JavaScript 向下滑动

[英]JavaScript slidedown without jQuery

我希望有与 jQuery 向下滑动类似的效果,但使用 jQuery 或任何其他库。 我知道这是“可能的”,因为 jQuery 中的任何内容都可以用纯 JavaScript 完成。 只是更难。

我不能使用 jQuery,因为所有内容都必须用我自己的代码编写,而无需使用任何库。

有没有人用纯 JavaScript 做过类似的事情或任何效果?

既然我们是在 2014 年,为什么不使用 CSS 过渡,只改变元素的 height 属性呢? 小提琴

CSS:

.wrapper {
    transition:height 1s ease-out;
    height:0;
    overflow:hidden;
}

HTML:

<div id="wrapper">
//content
</div>

JAVASCRIPT:

document.getElementById("wrapper").style.height = //content height +"px";

2020 EDIT(处理未知高度):

所以我们在 2020 年,更明显的是现在我们应该依赖 CSS 效果来制作这种动画。

但是,针对此答案提出了一个有效的观点 - 您需要指定您在 js 代码中设置动画的元素的高度,并且您可能事先不知道该值。

所以六年后,我添加了几行额外的代码来覆盖这个案例。

因此,如果我们使用与 2014 年旧示例相同的 CSS 和 HTML,这就是新的 JS。 新小提琴!

const slideDown = elem => elem.style.height = `${elem.scrollHeight}px`;

slideDown(document.getElementById("wrapper"));

这是我从头开始编写的一小段代码。
它纯粹是基于时间的

var minheight = 20;
var maxheight = 100;
var time = 1000;
var timer = null;
var toggled = false;

window.onload = function() {
    var controller = document.getElementById('slide');
    var slider = document.getElementById('slider');
    slider.style.height = minheight + 'px'; //not so imp,just for my example
    controller.onclick = function() {  
        clearInterval(timer);
        var instanceheight = parseInt(slider.style.height);  // Current height
        var init = (new Date()).getTime(); //start time
        var height = (toggled = !toggled) ? maxheight: minheight; //if toggled

        var disp = height - parseInt(slider.style.height);
        timer = setInterval(function() {
            var instance = (new Date()).getTime() - init; //animating time
            if(instance <= time ) { //0 -> time seconds
                var pos = instanceheight + Math.floor(disp * instance / time);
                slider.style.height =  pos + 'px';
            }else {
                slider.style.height = height + 'px'; //safety side ^^
                clearInterval(timer);
            }
        },1);
    };
};

在这里测试:http: //jsbin.com/azewi5/5

作为对@Ruben Serrate 解决方案的改进,它错过了未知高度的用例,我使用 CSS3 和 javascript(没有 jQuery)创建了这个:

/**
* getHeight - for elements with display:none
 */
getHeight = function(el) {
    var el_style      = window.getComputedStyle(el),
        el_display    = el_style.display,
        el_position   = el_style.position,
        el_visibility = el_style.visibility,
        el_max_height = el_style.maxHeight.replace('px', '').replace('%', ''),

        wanted_height = 0;


    // if its not hidden we just return normal height
    if(el_display !== 'none' && el_max_height !== '0') {
        return el.offsetHeight;
    }

    // the element is hidden so:
    // making the el block so we can meassure its height but still be hidden
    el.style.position   = 'absolute';
    el.style.visibility = 'hidden';
    el.style.display    = 'block';

    wanted_height     = el.offsetHeight;

    // reverting to the original values
    el.style.display    = el_display;
    el.style.position   = el_position;
    el.style.visibility = el_visibility;

    return wanted_height;
};


/**
* toggleSlide mimics the jQuery version of slideDown and slideUp
* all in one function comparing the max-heigth to 0
 */
toggleSlide = function(el) {
    var el_max_height = 0;

    if(el.getAttribute('data-max-height')) {
        // we've already used this before, so everything is setup
        if(el.style.maxHeight.replace('px', '').replace('%', '') === '0') {
            el.style.maxHeight = el.getAttribute('data-max-height');
        } else {
            el.style.maxHeight = '0';
        }
    } else {
        el_max_height                  = getHeight(el) + 'px';
        el.style['transition']         = 'max-height 0.5s ease-in-out';
        el.style.overflowY             = 'hidden';
        el.style.maxHeight             = '0';
        el.setAttribute('data-max-height', el_max_height);
        el.style.display               = 'block';

        // we use setTimeout to modify maxHeight later than display (to we have the transition effect)
        setTimeout(function() {
            el.style.maxHeight = el_max_height;
        }, 10);
    }
}

这是演示:http: //jsfiddle.net/pgfk2mvo/

如果您能找到任何改进,请告诉我,因为我总是尝试改进我的代码。 快乐编码! :D

可以用纯 JavaScript 完成。 只是更难。

其实也不算太难。 您只需要熟悉setTimeout() (无论如何这都是一个好主意,因为它教会了您 node.js 的编程风格)。 最简单的实现(没有 jQuery 的所有功能,留给读者作为家庭作业):

function slideDown (element, duration, finalheight, callback) {
    var s = element.style;
    s.height = '0px';

    var y = 0;
    var framerate = 10;
    var one_second = 1000;
    var interval = one_second*duration/framerate;
    var totalframes = one_second*duration/interval;
    var heightincrement = finalheight/totalframes;
    var tween = function () {
        y += heightincrement;
        s.height = y+'px';
        if (y<finalheight) {
            setTimeout(tween,interval);
        }
    }
    tween();
}

当然,这不是编写它的最短方法,您不必声明所有这些变量,如one_second等。我只是这样做是为了清楚地显示发生了什么。

这个例子也比试图阅读 jQuery 的源代码更短更容易理解。


有没有人用纯 JavaScript 做过类似的事情或任何效果?

哦,是的,当然,这是我周末为了好玩而做的那种事情:

这是使用具有未知内容高度元素的 slideDown、slideUp 动画的解决方案。 https://jsfiddle.net/gebpjo1L/18/

它基于 CSS 3 高度动画,但动画需要指定的内容高度,因此在展开内容之前需要通过 JavaScript 获取内容的高度。

 var container = document.querySelector('div') var button = document.querySelector('button') button.addEventListener('click', () => { /** Slide down. */ if(!container.classList.contains('active')) { /** Show the container. */ container.classList.add('active') container.style.height = "auto" /** Get the computed height of the container. */ var height = container.clientHeight + "px" /** Set the height of the content as 0px, */ /** so we can trigger the slide down animation. */ container.style.height = "0px" /** Do this after the 0px has applied. */ /** It's like a delay or something. MAGIC! */ setTimeout(() => { container.style.height = height }, 0) /** Slide up. */ } else { /** Set the height as 0px to trigger the slide up animation. */ container.style.height = "0px" /** Remove the `active` class when the animation ends. */ container.addEventListener('transitionend', () => { container.classList.remove('active') }, {once: true}) } })
 div { transition: height .5s ease; overflow : hidden; } div:not(.active) { display: none; }
 <div> I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. </div> <button>Slide Toggle</button>

早期答案的问题是您需要在开始之前知道高度。 很多时候你没有。 我建了一个幻灯片,你先建一个持有人的div,把对象放到幻灯片里面,把对象的显示设置为阻挡并获取高度,然后用它来做sind。 当侧面完成后,支架被移除,完成后向下滑动。 下面是例子。

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <div >
            <div id="test" style="height: 150px; width: 100px; background-color: yellowgreen; display:none">block</div>
        </div>
        <div>&nbsp;</div>
        <div onclick="slideUp(document.getElementById('test'));">slide Up</div>
        <div>&nbsp;</div>
        <div onclick="slideDown(document.getElementById('test'))">slide down</div>
        <script>
            function slideDown(obj, speed) {
                var mySpeed = speed || 300;
                var intervals = mySpeed / 30; // we are using 30 ms intervals
                alert('intervals = ' + intervals);
                var holder = document.createElement('div');//
                var parent = obj.parentNode;
                holder.setAttribute('style', 'height: 0px; overflow:hidden');
                parent.insertBefore(holder, obj);
                parent.removeChild(obj);
                holder.appendChild(obj);
                obj.style.display = obj.getAttribute("data-original-display") || "";
                var height = obj.offsetHeight;
                var sepHeight = height / intervals;
                //  alert(sepHeight)
                var timer = setInterval(function() {
                    var holderHeight = holder.offsetHeight;
                    if (holderHeight + sepHeight < height) {
                        holder.style.height = (holderHeight + sepHeight) + 'px';
                    } else {
                        // clean up
                        holder.removeChild(obj);
                        parent.insertBefore(obj, holder);
                        parent.removeChild(holder);
                        clearInterval(timer);
                    }
                }, 30);
            }

            function slideUp(obj, speed) {
                var mySpeed = speed || 300;
                var intervals = mySpeed / 30; // we are using 30 ms intervals
                var height = obj.offsetHeight;
                var holder = document.createElement('div');//
                var parent = obj.parentNode;
                holder.setAttribute('style', 'height: ' + height + 'px; overflow:hidden');
                parent.insertBefore(holder, obj);
                parent.removeChild(obj);
                holder.appendChild(obj);
                var originalDisplay = (obj.style.display !== 'none') ? obj.style.display : '';
                obj.setAttribute("data-original-display", originalDisplay);
                var sepHeight = height / intervals;
                //  alert(sepHeight)
                var timer = setInterval(function() {
                    var holderHeight = holder.offsetHeight;
                    console.log(holderHeight);
                    if (holderHeight - sepHeight > 0) {
                        holder.style.height = (holderHeight - sepHeight) + 'px';
                    } else {
                        // clean up
                        obj.style.display = 'none';
                        holder.removeChild(obj);
                        parent.insertBefore(obj, holder);
                        parent.removeChild(holder);
                        clearInterval(timer);
                    }
                }
                , 30);
            }

        </script>
    </body>
</html>

在修改了Symba 的答案后,我想出了这个来考虑填充和边框:

toggleSlide = function (el) {
    var el_max_height = 0;
    if (el.getAttribute('data-max-height')) {
        if (el.style.maxHeight.replace('px', '').replace('%', '') === '0') {
            el.style.maxHeight = el.getAttribute('data-max-height');
            el.style.paddingTop = el.getAttribute('data-pad-top');
            el.style.paddingBottom = el.getAttribute('data-pad-bottom');
            el.style.borderTop = el.getAttribute('data-border-top');
            el.style.borderBottom = el.getAttribute('data-border-bottom');
        } else {
            el.style.maxHeight = '0';
            el.style.paddingTop = '0';
            el.style.paddingBottom = '0';
            el.style.borderBottom = '0';
            el.style.borderTop = '0';
        }
    } else {
        el_max_height = getHeight(el) + 'px';
        el.style['transition-property'] = 'max-height, padding-top, padding-bottom, border-bottom, border-top';
        el.style['transition-duration'] = '0.5s';
        el.style['transition-timing-function'] = 'ease-in-out';
        el.style.overflowY = 'hidden';
        el.style.maxHeight = '0';
        el.setAttribute('data-max-height', el_max_height);
        el.setAttribute('data-pad-top', el.style.paddingTop);
        el.setAttribute('data-pad-bottom', el.style.paddingBottom);
        el.setAttribute('data-border-top', el.style.borderTop);
        el.setAttribute('data-border-bottom', el.style.borderBottom);
        el.style.display = 'block';
        setTimeout(function () { el.style.maxHeight = el_max_height; }, 10);
    }
}

当扩展过渡开始时,顶部边框有一点闪烁,我不知道(尚)如何解决这个问题。

我没有修改 Symba 的 getHeight 函数; 为此,请参阅他的答案。

这个纯 js 解决方案不需要事先知道最大高度,并使用 css-transitions。

它将在过渡期间将 max-height 设置为浏览器窗口高度的 2 倍,然后在过渡完成后删除 max-height。

完整的 slideDown、slideUp 演示 @ https://kaizhu256.github.io/node-swagger-lite/build..alpha..travis-ci.org/app/index.html

css

.swggAnimateSlide {
    overflow-y: hidden;
    transition: all 500ms linear;
}
.swggAnimateSlideUp {
    border-bottom: 0 !important;
    border-top: 0 !important;
    margin-bottom: 0 !important;
    margin-top: 0 !important;
    max-height: 0 !important;
    padding-bottom: 0 !important;
    padding-top: 0 !important;
}

js

domAnimateSlideDown = function (element) {
/*
 * this function will slideDown the dom-element
 */
    if (element.style.maxHeight || element.style.display !== 'none') {
        return;
    }
    element.classList.add('swggAnimateSlideUp');
    element.classList.add('swggAnimateSlide');
    element.style.display = '';
    setTimeout(function () {
        element.style.maxHeight = 2 * window.innerHeight + 'px';
        element.classList.remove('swggAnimateSlideUp');
    }, 50);
    setTimeout(function () {
        element.style.maxHeight = '';
        element.classList.remove('swggAnimateSlide');
    }, 500);
};


domAnimateSlideUp = function (element) {
/*
 * this function will slideUp the dom-element
 */
    if (element.style.maxHeight || element.style.display === 'none') {
        return;
    }
    element.style.maxHeight = 2 * window.innerHeight + 'px';
    element.classList.add('swggAnimateSlide');
    setTimeout(function () {
        element.classList.add('swggAnimateSlideUp');
        element.style.maxHeight = '0px';
    }, 50);
    setTimeout(function () {
        element.style.display = 'none';
        element.style.maxHeight = '';
        element.classList.remove('swggAnimateSlide');
        element.classList.remove('swggAnimateSlideUp');
    }, 500);
};

我花了很多时间来开发这些函数,使它们与JQuery的最相似,我认为它们是morph函数中需要优化的一些细节,主要是长度

 function morph( element, options, animationTime, callback ) { // options is an array with the same structural of Properties and DiffValues and which contains the properties values we want the animation make var ComputedElementStyle = window.getComputedStyle(element,null); var AttrElementStyle = element.style; var Properties = { // the actuals computed properties width: parseInt( ComputedElementStyle.getPropertyValue("width")), height: parseInt( ComputedElementStyle.getPropertyValue("height")), padding: { top: parseInt(ComputedElementStyle.getPropertyValue("padding-top")), right: parseInt(ComputedElementStyle.getPropertyValue("padding-right")), bot: parseInt(ComputedElementStyle.getPropertyValue("padding-bottom")), left: parseInt(ComputedElementStyle.getPropertyValue("padding-left")) }, margin:{ top: parseInt(ComputedElementStyle.getPropertyValue("margin-top")), right: parseInt(ComputedElementStyle.getPropertyValue("margin-right")), bot: parseInt(ComputedElementStyle.getPropertyValue("margin-bottom")), left: parseInt(ComputedElementStyle.getPropertyValue("margin-left")) } }; var DiffValues = { // the differences between actual properties values and values we want to width: (options['width']!=null) ? (options['width'] - Properties['width']) : 0, height: (options['height']!=null) ? (options['height'] - Properties['height']) : 0, padding: { top: (options['padding']&&options['padding']['top']!=null) ? options['padding']['top'] - Properties['padding']['top'] : 0, right: (options['padding']&&options['padding']['right']!=null) ? options['padding']['right'] - Properties['padding']['right'] : 0, bot: (options['padding']&&options['padding']['bot']!=null) ? options['padding']['bot'] - Properties['padding']['bot'] : 0, left: (options['padding']&&options['padding']['left']!=null) ? options['padding']['left'] - Properties['padding']['left'] : 0 }, margin:{ top: (options['margin']&&options['margin']['top']!=null) ? options['margin']['top'] - Properties['margin']['top'] : 0, right: (options['margin']&&options['margin']['right']!=null) ? options['margin']['right'] - Properties['margin']['right'] : 0, bot: (options['margin']&&options['margin']['bot']!=null) ? options['margin']['bot'] - Properties['margin']['bot'] : 0, left: (options['margin']&&options['margin']['left']!=null) ? options['margin']['left'] - Properties['margin']['left'] : 0 } }; var beginTime = new Date().getTime(); // time at begining of animation animationTime = (animationTime!=null) ? animationTime : 250; AttrElementStyle.overflow = "hidden"; // disable the potentials scrollbars var sinceBeginTime; // time since the begining var progressFactor; // coeficient that correspond to the advancement of the animation timer = setInterval(function() { // begin of the animation sinceBeginTime = new Date().getTime() - beginTime; if( sinceBeginTime < animationTime ) { progressFactor = sinceBeginTime / animationTime; AttrElementStyle.width=(Properties['width'] + DiffValues['width'] * progressFactor) +"px"; AttrElementStyle.height=(Properties['height'] + DiffValues['height'] * progressFactor) +"px"; AttrElementStyle.padding= (Properties['padding']['top'] + DiffValues['padding']['top'] * progressFactor) +"px "+ (Properties['padding']['right'] + DiffValues['padding']['right'] * progressFactor) +"px "+ (Properties['padding']['bot'] + DiffValues['padding']['bot'] * progressFactor) +"px "+ (Properties['padding']['left'] + DiffValues['padding']['left'] * progressFactor) +"px"; AttrElementStyle.margin= (Properties['margin']['top'] + DiffValues['margin']['top'] * progressFactor) +"px "+ (Properties['margin']['right'] + DiffValues['margin']['right'] * progressFactor) +"px "+ (Properties['margin']['bot'] + DiffValues['margin']['bot'] * progressFactor) +"px "+ (Properties['margin']['left'] + DiffValues['margin']['left'] * progressFactor) +"px"; }else { AttrElementStyle.width=options['width'] +"px"; AttrElementStyle.height=options['height'] +"px"; AttrElementStyle.padding= (Properties['padding']['top'] + DiffValues['padding']['top']) +"px "+ (Properties['padding']['right'] + DiffValues['padding']['right']) +"px "+ (Properties['padding']['bot'] + DiffValues['padding']['bot']) +"px "+ (Properties['padding']['left'] + DiffValues['padding']['left']) +"px"; AttrElementStyle.margin= (Properties['margin']['top'] + DiffValues['margin']['top']) +"px "+ (Properties['margin']['right'] + DiffValues['margin']['right']) +"px "+ (Properties['margin']['bot'] + DiffValues['margin']['bot']) +"px "+ (Properties['margin']['left'] + DiffValues['margin']['left']) +"px"; clearInterval( timer ); // end of the animation if( callback!=null ) // if there is a CALLBACK then call it callback(Properties); } },15); } function slideUp( element, animationTime , callback) { morph( element, { height:0, padding:{ top:0, bot:0 }, margin:{ top:0, bot:0 } }, animationTime, function(Properties) { // at the end of the slideUp we display: none the element and clean the other properties from style attribute var AttrElementStyle = element.style; AttrElementStyle.width=""; AttrElementStyle.height=""; AttrElementStyle.padding=""; AttrElementStyle.margin=""; element.style.display = 'none'; if(callback) callback(); }); } function slideDown( element, animationTime , callback) { var AttrElementStyle = element.style; var ComputedElementStyle = window.getComputedStyle(element,null); AttrElementStyle.display="block"; var options = { // the computed properties when element is displayed width: parseInt( ComputedElementStyle.getPropertyValue("width")), height: parseInt( ComputedElementStyle.getPropertyValue("height")), padding: { top: parseInt(ComputedElementStyle.getPropertyValue("padding-top")), bot: parseInt(ComputedElementStyle.getPropertyValue("padding-bottom")) }, margin:{ top: parseInt(ComputedElementStyle.getPropertyValue("margin-top")), bot: parseInt(ComputedElementStyle.getPropertyValue("margin-bottom")) } }; // after getting the actuals properties values of the element we flatten it AttrElementStyle.height="0"; AttrElementStyle.paddingTop="0"; AttrElementStyle.paddingBottom="0"; AttrElementStyle.marginTop="0"; AttrElementStyle.marginBottom="0"; morph( element, options , animationTime, function() { // morph the element from flat to the options properties that are right // at the end of slideDown we clean up the style but keep the display: block if the element is display: none in the stylesheet AttrElementStyle.width=""; AttrElementStyle.height=""; AttrElementStyle.padding=""; AttrElementStyle.margin=""; element.style.display = 'block'; if(callback) // if there is a CALLBACK then call it (we are in the morph() callback) callback(); }) }
 p{ width:50%; height:auto; padding:40px 0 10px; margin: 10px 0 40px; border:2px solid red } div{ border:1px solid blue; padding:10px }
 <div> <p id='p'>loiloilozoifboiygdouhbodihfgsd</br>rihgdggisbifghbsoifnsf</br>giodsbgsigfbsjgsgs</p> </div> <button onClick="slideUp( document.getElementById('p') ,500)">testUp</button> <button onClick="slideDown( document.getElementById('p') ,500)">testDown</button> <button id='btn1' onClick="morph( document.getElementById('btn1'),{ width: 120,height:130,padding:{top:70,left:50},margin:{right:40}} ,1000)">morphIt</button> <button onClick="document.getElementById('btn1').style=''">reset btn1</button>

让它更短:

<button onclick="slide()"></button>

<div id="box"></div>

JS:

slide(){document.getElementById("box").classList.toggle('hide');}

#box {
  overflow: hidden;
  transition: all .5s ease-in-out;
  -webkit-transition: all 0.5s ease-in-out;
  -moz-transition: all 0.5s ease-in-out;
  -ms-transition: all 0.5s ease-in-out;
  -o-transition: all 0.5s ease-in-out;
}
.hide {
  height: 0px !important;
}

基于Yami Odymels 的答案是一个版本,它考虑了顶部和底部填充,更少依赖预先在 css 中设置的值,并且在完成动画后还删除了内联样式,从而防止了视口调整大小的问题。

 const container = document.querySelector('.the-wrap') const button = document.querySelector('.the-button') button.addEventListener('click', function() { // slide down if(!container.classList.contains('active')) { // show/remove display: none; container.classList.add('active') // get current height const height = container.clientHeight + "px" // remove height to 0 container.style.height = 0; container.style.paddingTop = 0; container.style.paddingBottom = 0; container.style.overflow = "hidden"; // this triggers applying the height and padding-values; does not show up on screen setTimeout(() => { // register removing properties after end of transition container.addEventListener('transitionend', () => { container.style.height = ""; container.style.transition = ""; container.style.overflow = ""; }, {once: true}) // set transition container.style.transition = "height 0.5s ease, padding-top 0.5s ease, padding-bottom 0.5s ease"; // set values to trigger transition container.style.height = height; container.style.paddingTop = ""; container.style.paddingBottom = ""; }, 0); } // slide up else { // explicitly set height, transitioning to auto-values is not included in spec container.style.height = container.clientHeight + "px"; container.style.overflow = "hidden"; container.style.transition = "height 0.5s ease, padding-top 0.5s ease, padding-bottom 0.5s ease"; // again wait setTimeout(() => { // register cleanup container.addEventListener('transitionend', () => { container.classList.remove('active') container.style.height = ""; container.style.transition = ""; container.style.overflow = ""; container.style.paddingTop = ""; container.style.paddingBottom = ""; }, {once: true}); // set to visually hidden, thus triggering transition container.style.height = 0; container.style.paddingTop = 0; container.style.paddingBottom = 0; }, 0); } });
 *, *:before, *:after { box-sizing: border-box; } .the-wrap { outline: 2px solid crimson; margin-top: 50px; padding: 20px; } .the-wrap:not(.active) { display: none; }
 <button class="the-button">toggle</button> <div class="the-wrap"> <h1>Headline</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> </div>

查看 该效果的 jQuery 源代码slideDown调用show )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM