验证码刷新时SVG加载动画触发NS_BINDING_ABORTED错误的解决
验证码刷新时SVG加载动画无法显示的问题排查与解决
我写了一段JavaScript代码,用于验证码图片刷新时展示加载动画。用GIF格式的加载动画时一切正常,但换成Animated SVG后,加载动画完全不显示,FireFox的网络控制台还返回NS_BINDING_ABORTED错误。
问题代码
JavaScript代码
$(document).ready(function(){ $("#captchaImg").click(function(e){ e.preventDefault(); src = $(this).children('img').attr('src'); width = '{{config('captcha.flat.width')}}px'; height= '{{config('captcha.flat.height')}}px'; console.log(width, height) // 下面这行把gif换成svg就出问题 $(this).children('img').attr({'src':'/imgs/loading.gif','width':width, 'height': height}) src = src.replace(/&t=.*/,'') t = new Date(); $(this).children('img').attr('src',src+"&t="+t.getTime()); $( "#randQuote" ).text( '{{__('Loading')}}' ); $.ajax({ url: "/rand-quote", cache: false }) .done(function( data ) { console.log(data.msg) $( "#randQuote" ).text( data.msg ); return false; }); }) })
使用的Animated SVG代码
<?xml version="1.0" encoding="utf-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="64px" height="64px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"> <g transform="rotate(0 50 50)"> <rect x="37.5" y="5" rx="4.76" ry="4.76" width="25" height="34" fill="#482173"> <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.875s" repeatCount="indefinite"></animate> </rect> </g><g transform="rotate(45 50 50)"> <rect x="37.5" y="5" rx="4.76" ry="4.76" width="25" height="34" fill="#2e6f8e"> <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.75s" repeatCount="indefinite"></animate> </rect> </g><g transform="rotate(90 50 50)"> <rect x="37.5" y="5" rx="4.76" ry="4.76" width="25" height="34" fill="#29af7f"> <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.625s" repeatCount="indefinite"></animate> </rect> </g><g transform="rotate(135 50 50)"> <rect x="37.5" y="5" rx="4.76" ry="4.76" width="25" height="34" fill="#bddf26"> <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5s" repeatCount="indefinite"></animate> </rect> </g><g transform="rotate(180 50 50)"> <rect x="37.5" y="5" rx="4.76" ry="4.76" width="25" height="34" fill="#482173"> <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.375s" repeatCount="indefinite"></animate> </rect> </g><g transform="rotate(225 50 50)"> <rect x="37.5" y="5" rx="4.76" ry="4.76" width="25" height="34" fill="#2e6f8e"> <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.25s" repeatCount="indefinite"></animate> </rect> </g><g transform="rotate(270 50 50)"> <rect x="37.5" y="5" rx="4.76" ry="4.76" width="25" height="34" fill="#29af7f"> <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.125s" repeatCount="indefinite"></animate> </rect> </g><g transform="rotate(315 50 50)"> <rect x="37.5" y="5" rx="4.76" ry="4.76" width="25" height="34" fill="#bddf26"> <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animate> </rect> </g> <!-- [ldio] generated by loading.io --></svg>
想请教下,这种场景下GIF和SVG的差异是什么?该怎么解决这个问题?
问题原因与解决方法
GIF与SVG的核心差异
- 加载渲染机制不同:GIF是位图格式,浏览器加载时会快速解析帧数据,哪怕后续立刻替换src,已加载的帧也能正常显示;而Animated SVG是XML结构的矢量图,动画依赖浏览器的SVG解析引擎和DOM渲染流程,加载、解析、初始化动画需要更长时间。
- 请求中断影响不同:当你快速替换img的src时,GIF的请求即使被中断,已加载的部分足够显示基础帧;但SVG请求被中断后,浏览器无法完整解析XML结构,自然渲染不出任何内容,这就是FireFox返回
NS_BINDING_ABORTED的原因——SVG请求还没完成就被新的验证码请求打断了。
解决方法
方法1:延迟替换验证码图片的src
给SVG留足加载和初始化的时间,用setTimeout延迟替换验证码的src:
$(document).ready(function(){ $("#captchaImg").click(function(e){ e.preventDefault(); const $img = $(this).children('img'); const src = $img.attr('src'); const width = '{{config('captcha.flat.width')}}px'; const height= '{{config('captcha.flat.height')}}px'; $img.attr({'src':'/imgs/loading.svg','width':width, 'height': height}); $( "#randQuote" ).text( '{{__('Loading')}}' ); // 延迟替换验证码src,给SVG加载时间 setTimeout(() => { const newSrc = src.replace(/&t=.*/,'') + "&t=" + new Date().getTime(); $img.attr('src', newSrc); }, 100); // 可根据实际情况调整延迟时间 $.ajax({ url: "/rand-quote", cache: false }) .done(function( data ) { console.log(data.msg) $( "#randQuote" ).text( data.msg ); }); }) })
方法2:将SVG内联到页面中
把SVG代码直接嵌入页面作为隐藏元素,切换时显示它而非通过img标签加载:
<!-- 页面中预先嵌入SVG --> <div id="loadingSvg" style="display:none; width: {{config('captcha.flat.width')}}px; height: {{config('captcha.flat.height')}}px;"> <?xml version="1.0" encoding="utf-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="64px" height="64px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"> <!-- 完整SVG代码 --> </svg> </div> <!-- 验证码容器结构 --> <div id="captchaImg"> <img src="验证码地址" alt="验证码"> </div>
对应的JS修改:
$(document).ready(function(){ $("#captchaImg").click(function(e){ e.preventDefault(); const $captchaContainer = $(this); const $img = $captchaContainer.children('img'); const src = $img.attr('src'); // 隐藏验证码图片,显示内联SVG $img.hide(); const $loadingSvg = $('#loadingSvg').clone().show(); $captchaContainer.append($loadingSvg); $( "#randQuote" ).text( '{{__('Loading')}}' ); const newSrc = src.replace(/&t=.*/,'') + "&t=" + new Date().getTime(); // 监听验证码加载完成,替换回图片并移除SVG $img.one('load', function() { $loadingSvg.remove(); $img.show(); }).attr('src', newSrc); $.ajax({ url: "/rand-quote", cache: false }) .done(function( data ) { console.log(data.msg) $( "#randQuote" ).text( data.msg ); }); }) })
方法3:用CSS动画替代Animated SVG
如果SVG的问题不好处理,也可以用纯CSS实现加载动画,直接应用到占位元素上,避免图片加载的请求中断问题。
内容的提问来源于stack exchange,提问作者SaidbakR
相关产品推荐
相关产品推荐

